lazarus/lcl/dbgrids.pas
Maxim Ganetsky 53aaae9c5e Revert "Grids: uses const paramterers for strings where applicable. Patch by Alexey Torgashin. Issue #39851."
This reverts commit 72344a65f7.

These changes break compatibility (change signatures of virtual
methods), potentially unsafe (these strings can be potentially changed
through user callbacks and this will lead to crashes) and provide very
little (if at all) performance improvements.

(cherry picked from commit dbfbdee461)
2023-07-08 18:58:24 +03:00

4845 lines
135 KiB
ObjectPascal

{ $Id$}
{
/***************************************************************************
DBGrids.pas
-----------
An interface to DB aware Controls
Initial Revision : Sun Sep 14 2003
***************************************************************************/
*****************************************************************************
This file is part of the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
}
{
TDBGrid and TComponentDataLink for Lazarus
Copyright (C) 2003 Jesus Reyes Aguilar.
email: jesusrmx@yahoo.com.mx
TComponentDatalink idea was taken from Joanna Carter's article
"The Ultimate Datalink?" Delphi Magazine Issue #30 February 1998
}
unit DBGrids;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Math, FileUtil, DB, LazUTF8, LazLoggerBase, LCLStrConsts,
LCLIntf, LCLType, LMessages, LResources, Controls, StdCtrls, Graphics, Grids,
Dialogs, Themes, Variants, Clipbrd, ImgList, Laz2_XMLCfg;
{$if FPC_FULLVERSION<20701}
{$DEFINE noautomatedbookmark}
{$endif}
const
DEFINDICATORCOLWIDTH = 12;
type
TCustomDbGrid = class;
TColumn = class;
EInvalidGridOperation = class(Exception);
TDBGridOption = (
dgEditing, // Enable or disable editing data
dgTitles, // Show column titles
dgIndicator, // Show current row indicator
dgColumnResize,
dgColumnMove,
dgColLines, // Show vertical lines between columns
dgRowLines, // Show horizontal lines between rows
dgTabs, // Allow using TAB key to navigate grid
dgAlwaysShowEditor,
dgRowSelect,
dgAlwaysShowSelection,
dgConfirmDelete,
dgCancelOnExit,
dgMultiselect, // Allow selection of multiple nonadjacent rows
dgHeaderHotTracking,
dgHeaderPushedLook,
dgPersistentMultiSelect,
dgAutoSizeColumns,
dgAnyButtonCanSelect, // any mouse button can move selection
dgDisableDelete, // disable deleting records with Ctrl+Delete
dgDisableInsert, // disable inserting (or append) records
dgCellHints, // show individual cell hints
dgTruncCellHints, // show cell hints if cell text is too long
dgCellEllipsis, // show ... if cell text is truncated
dgRowHighlight, // Highlight current row
dgThumbTracking,
dgDblClickAutoSize, // dblclicking columns borders (on hdrs) resize col.
dgDisplayMemoText // show memo content instead of (memo) for ftMemo fields
);
TDbGridOptions = set of TDbGridOption;
TDbGridExtraOption = (
dgeAutoColumns, // if uncustomized columns, add them anyway?
dgeCheckboxColumn // enable the use of checkbox in columns
);
TDbGridExtraOptions = set of TDbGridExtraOption;
TDbGridStatusItem = (gsUpdatingData, gsAddingAutoColumns, gsRemovingAutoColumns,
gsAutoSized, gsStartEditing, gsLoadingGrid);
TDbGridStatus = set of TDbGridStatusItem;
TDataSetScrolledEvent =
procedure(DataSet: TDataSet; Distance: Integer) of object;
TFocusControlEvent =
procedure(aField: TFieldRef) of object;
TDBGridClickEvent =
procedure(Column: TColumn) of object;
TMovedEvent =
procedure(Sender: TObject; FromIndex, ToIndex: Integer) of object;
TDrawColumnCellEvent =
procedure(Sender: TObject; const Rect: TRect; DataCol: Integer;
Column: TColumn; State: TGridDrawState) of object;
TGetDbEditMaskEvent =
procedure (Sender: TObject; const Field: TField;
var Value: string) of object;
TDbGridSelEditorEvent =
procedure(Sender: TObject; Column: TColumn;
var Editor: TWinControl) of object;
TPrepareDbGridCanvasEvent =
procedure(sender: TObject; DataCol: Integer;
Column: TColumn; AState: TGridDrawState) of object;
TDbGridCheckBoxBitmapEvent =
procedure(Sender: TObject; const CheckedState: TCheckboxState;
var ABitmap: TBitmap) of object;
TDbGridCheckboxStateEvent =
procedure(Sender: TObject; Column: TColumn;
var AState: TCheckboxState) of object;
TDbGridCellHintEvent =
procedure(Sender: TObject; Column: TColumn; var AText: String) of object;
type
TBookmarkList = class;
TBookmarkedRecordEnumeratorOptions = set of
(
breDisableDataset,
breStopOnInvalidBookmark,
breRestoreCurrent
);
{ TBookmarkedRecordEnumerator }
TBookmarkedRecordEnumerator = class
private
fBookmarkList: TBookmarkList;
fBookmarkIndex: Integer;
fCurrent, fBook: TBookmark;
fDataset: TDataset;
fOptions: TBookmarkedRecordEnumeratorOptions;
public
constructor Create(bookList: TBookmarkList; aGrid: TCustomDbGrid;
anOptions: TBookmarkedRecordEnumeratorOptions);
destructor Destroy; override;
function MoveNext: boolean;
function GetEnumerator: TBookmarkedRecordEnumerator;
property Current: TBookmark read fCurrent;
property Options: TBookmarkedRecordEnumeratorOptions read fOptions write fOptions;
end;
{ TBookmarkList }
TBookmarkList = class
private
FList: TFPList; // list of TBookmark
FGrid: TCustomDbGrid;
FDataset: TDataset;
FCanDoBinarySearch: boolean;
function GetCount: integer;
function GetCurrentRowSelected: boolean;
function GetItem(AIndex: Integer): TBookmark;
procedure SetCurrentRowSelected(const AValue: boolean);
procedure CheckActive;
public
constructor Create(AGrid: TCustomDbGrid);
destructor Destroy; override;
procedure Clear;
procedure Delete;
function Find(const Item: TBookmark; var AIndex: Integer): boolean;
function IndexOf(const Item: TBookmark): Integer;
function Refresh: boolean;
function GetEnumerator(opt: TBookmarkedRecordEnumeratorOptions =
[breDisableDataset, breRestoreCurrent]): TBookmarkedRecordEnumerator;
property Count: integer read GetCount;
property CurrentRowSelected: boolean
read GetCurrentRowSelected write SetCurrentRowSelected;
property Items[AIndex: Integer]: TBookmark read GetItem; default;
end;
{ TComponentDataLink }
TComponentDataLink=class(TDatalink)
private
FDataSet: TDataSet;
FDataSetName: string;
FModified: Boolean;
FOnDatasetChanged: TDatasetNotifyEvent;
fOnDataSetClose: TDataSetNotifyEvent;
fOnDataSetOpen: TDataSetNotifyEvent;
FOnDataSetScrolled: TDataSetScrolledEvent;
FOnEditingChanged: TDataSetNotifyEvent;
fOnFocusControl: TFocusControlEvent;
fOnInvalidDataSet: TDataSetNotifyEvent;
fOnInvalidDataSource: TDataSetNotifyEvent;
FOnLayoutChanged: TDataSetNotifyEvent;
fOnNewDataSet: TDataSetNotifyEvent;
FOnRecordChanged: TFieldNotifyEvent;
FOnUpdateData: TDataSetNotifyEvent;
function GetDataSetName: string;
function GetFields(Index: Integer): TField;
procedure SetDataSetName(const AValue: string);
protected
procedure RecordChanged(Field: TField); override;
procedure DataSetChanged; override;
procedure ActiveChanged; override;
procedure LayoutChanged; override;
procedure DataSetScrolled(Distance: Integer); override;
procedure FocusControl(Field: TFieldRef); override;
// Testing Events
procedure CheckBrowseMode; override;
procedure EditingChanged; override;
procedure UpdateData; override;
function MoveBy(Distance: Integer): Integer; override;
property Modified: Boolean read FModified write FModified;
public
property OnRecordChanged: TFieldNotifyEvent read FOnRecordChanged write FOnRecordChanged;
property OnDataSetChanged: TDatasetNotifyEvent read FOnDatasetChanged write FOnDataSetChanged;
property OnNewDataSet: TDataSetNotifyEvent read fOnNewDataSet write fOnNewDataSet;
property OnDataSetOpen: TDataSetNotifyEvent read fOnDataSetOpen write fOnDataSetOpen;
property OnInvalidDataSet: TDataSetNotifyEvent read fOnInvalidDataSet write fOnInvalidDataSet;
property OnInvalidDataSource: TDataSetNotifyEvent read fOnInvalidDataSource write fOnInvalidDataSource;
property OnFocusControl: TFocusControlEvent read fOnFocusControl write fOnFocusControl;
property OnLayoutChanged: TDataSetNotifyEvent read FOnLayoutChanged write FOnLayoutChanged;
property OnDataSetClose: TDataSetNotifyEvent read fOnDataSetClose write fOnDataSetClose;
property OnDataSetScrolled: TDataSetScrolledEvent read FOnDataSetScrolled write FOnDataSetScrolled;
property OnEditingChanged: TDataSetNotifyEvent read FOnEditingChanged write FOnEditingChanged;
property OnUpdateData: TDataSetNotifyEvent read FOnUpdateData write FOnUpdateData;
property DataSetName:string read GetDataSetName write SetDataSetName;
property Fields[Index: Integer]: TField read GetFields;
property VisualControl;
end;
{ TColumn }
TColumnTitle = class(TGridColumnTitle)
protected
function GetDefaultCaption: string; override;
end;
{ TColumn }
TColumn = class(TGridColumn)
private
FDisplayFormat: String;
FDisplayFormatChanged: boolean;
FFieldName: String;
FField: TField;
FIsAutomaticColumn: boolean;
FDesignIndex: Integer;
procedure ApplyDisplayFormat;
function GetDataSet: TDataSet;
function GetDisplayFormat: string;
function GetField: TField;
function GetIsDesignColumn: boolean;
function IsDisplayFormatStored: boolean;
procedure SetDisplayFormat(const AValue: string);
procedure SetField(const AValue: TField);
procedure SetFieldName(const AValue: String);
protected
function CreateTitle: TGridColumnTitle; override;
function GetDefaultAlignment: TAlignment; override;
function GetDefaultDisplayFormat: string;
function GetDefaultValueChecked: string; override;
function GetDefaultValueUnchecked: string; override;
function GetDefaultVisible: boolean; override;
function GetDisplayName: string; override;
function GetDefaultReadOnly: boolean; override;
function GetDefaultWidth: Integer; override;
function GetPickList: TStrings; override;
property IsAutomaticColumn: boolean read FIsAutomaticColumn;
property IsDesignColumn: boolean read GetIsDesignColumn;
procedure LinkField;
public
constructor Create(ACollection: TCollection); override;
procedure Assign(Source: TPersistent); override;
function IsDefault: boolean; override;
property DesignIndex: integer read FDesignIndex;
property Field: TField read GetField write SetField;
published
property FieldName: String read FFieldName write SetFieldName;
property DisplayFormat: string read GetDisplayFormat write SetDisplayFormat
stored IsDisplayFormatStored;
end;
TColumnOrder = (coDesignOrder, coFieldIndexOrder);
{ TDBGridColumns }
TDBGridColumns = class(TGridColumns)
private
function GetColumn(Index: Integer): TColumn;
procedure SetColumn(Index: Integer; Value: TColumn);
protected
procedure Update(Item: TCollectionItem); override;
function ColumnFromField(Field: TField): TColumn;
function HasAutomaticColumns: boolean;
function HasDesignColumns: boolean;
procedure RemoveAutoColumns;
public
function Add: TColumn;
function ColumnByFieldname(const aFieldname: string): TColumn;
function ColumnByTitle(const aTitle: string): TColumn;
procedure LinkFields;
procedure ResetColumnsOrder(ColumnOrder: TColumnOrder);
property Items[Index: Integer]: TColumn read GetColumn write SetColumn; default;
end;
{ TCustomDBGrid }
TCustomDBGrid=class(TCustomGrid)
private
FDataLink: TComponentDataLink;
FExtraOptions: TDBGridExtraOptions;
FOnCellClick: TDBGridClickEvent;
FOnColEnter,FOnColExit: TNotifyEvent;
FOnColumnMoved: TMovedEvent;
FOnColumnSized: TNotifyEvent;
FOnDrawColumnCell: TDrawColumnCellEvent;
FOnDrawColumnTitle: TDrawColumnCellEvent;
FOnFieldEditMask: TGetDbEditMaskEvent;
FOnTitleClick: TDBGridClickEvent;
FOnSelectEditor: TDbGridSelEditorEvent;
FOnCheckboxBitmap: TDbGridCheckBoxBitmapEvent;
FOnCheckboxState: TDbGridCheckboxStateEvent;
FOptions: TDBGridOptions;
FReadOnly: Boolean;
FColEnterPending: Boolean;
FLayoutChangedCount: integer;
FTempText : string;
FDrawingActiveRecord: Boolean;
FDrawingMultiSelRecord: Boolean;
FDrawingEmptyDataset: Boolean;
FEditingColumn: Integer;
FOldPosition: Integer;
FDefaultColWidths: boolean;
FGridStatus: TDBGridStatus;
FOldControlStyle: TControlStyle;
FSelectedRows: TBookmarkList;
FOnPrepareCanvas: TPrepareDbGridCanvasEvent;
FKeySign: Integer;
FSavedRecord: Integer;
FOnGetCellHint: TDbGridCellHintEvent;
FOnRowMoved: TMovedEvent;
FFixedRowsExtra: Integer;
procedure EmptyGrid;
function GetColumns: TDBGridColumns;
function GetCurrentColumn: TColumn;
function GetCurrentField: TField;
function GetDataSource: TDataSource;
function GetFirstColumn: TColumn;
function GetLastColumn: TColumn;
function GetRecordCount: Integer;
function GetSelectedFieldRect: TRect;
function GetSelectedIndex: Integer;
procedure OnRecordChanged(Field:TField);
procedure OnDataSetChanged(aDataSet: TDataSet);
procedure OnDataSetOpen(aDataSet: TDataSet);
procedure OnDataSetClose(aDataSet: TDataSet);
procedure OnEditingChanged(aDataSet: TDataSet);
procedure OnInvalidDataSet(aDataSet: TDataSet);
procedure OnInvalidDataSource(aDataSet: TDataset);
procedure OnFocusControl(aField: TFieldRef);
procedure OnLayoutChanged(aDataSet: TDataSet);
procedure OnNewDataSet(aDataSet: TDataset);
procedure OnDataSetScrolled(aDataSet:TDataSet; Distance: Integer);
procedure OnUpdateData(aDataSet: TDataSet);
procedure SetColumns(const AValue: TDBGridColumns);
//procedure ReadColumns(Reader: TReader);
//procedure SetColumns(const AValue: TDBGridColumns);
procedure SetCurrentField(const AValue: TField);
procedure SetDataSource(const AValue: TDataSource);
procedure SetExtraOptions(const AValue: TDBGridExtraOptions);
procedure SetFixedRowsExtra(AValue: Integer);
procedure SetOptions(const AValue: TDBGridOptions);
procedure SetRowMoved(AValue: TMovedEvent);
procedure SetSelectedIndex(const AValue: Integer);
procedure UpdateBufferCount;
// Temporal
function GetColumnCount: Integer;
function DefaultFieldColWidth(F: TField): Integer;
procedure UpdateGridColumnSizes;
procedure UpdateScrollbarRange;
procedure DoLayoutChanged;
//procedure WriteColumns(Writer: TWriter);
procedure RestoreEditor;
function ISEOF: boolean;
function ValidDataSet: boolean;
function InsertCancelable: boolean;
procedure StartUpdating;
procedure EndUpdating;
function UpdatingData: boolean;
procedure SwapCheckBox;
procedure ToggleSelectedRow;
procedure SelectRecord(AValue: boolean);
procedure GetScrollbarParams(out aRange, aPage, aPos: Integer);
procedure CMGetDataLink(var Message: TLMessage); message CM_GETDATALINK;
procedure ClearSelection(selCurrent:boolean=false);
function NeedAutoSizeColumns: boolean;
procedure RenewColWidths;
procedure InternalAutoSizeColumn(aCol: Integer; aCanvas: TCanvas; aDatalinkActive: Boolean);
procedure DoHeaderClick(Index: Integer);
protected
procedure AddAutomaticColumns;
procedure AssignTo(Dest: TPersistent); override;
procedure AutoAdjustColumn(aCol: Integer); override;
procedure BeforeMoveSelection(const DCol,DRow: Integer); override;
procedure BeginLayout;
procedure CellClick(const aCol,aRow: Integer; const Button:TMouseButton); override;
function CheckDisplayMemo(aField: TField): boolean;
procedure InvalidateSizes;
procedure ColRowMoved(IsColumn: Boolean; FromIndex,ToIndex: Integer); override;
function ColumnEditorStyle(aCol: Integer; F: TField): TColumnButtonStyle;
function CreateColumns: TGridColumns; override;
procedure CreateWnd; override;
procedure DefineProperties(Filer: TFiler); override;
procedure DefaultDrawCell(aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState);
function DefaultEditorStyle(const Style:TColumnButtonStyle; const F:TField): TColumnButtonStyle;
procedure DoCopyToClipboard; override;
procedure DoExit; override;
function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
procedure DoOnChangeBounds; override;
procedure DoPrepareCanvas(aCol,aRow:Integer; aState: TGridDrawState); override;
procedure DoLoadColumn(sender: TCustomGrid; aColumn: TGridColumn; aColIndex: Integer;
aCfg: TXMLConfig; aVersion: Integer; aPath: string); override;
procedure DoSaveColumn(sender: TCustomGrid; aColumn: TGridColumn; aColIndex: Integer;
aCfg: TXMLConfig; aVersion: Integer; aPath: string); override;
procedure DrawAllRows; override;
procedure DrawFocusRect(aCol,aRow:Integer; ARect:TRect); override;
procedure DrawRow(ARow: Integer); override;
procedure DrawCell(aCol,aRow: Integer; aRect: TRect; aState:TGridDrawState); override;
procedure DrawCellBackground(aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState);
procedure DrawCheckboxBitmaps(aCol: Integer; aRect: TRect; F: TField);
procedure DrawFixedText(aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState);
procedure DrawColumnText(aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState); override;
procedure DrawIndicator(ACanvas: TCanvas; R: TRect; Opt: TDataSetState; MultiSel: boolean); virtual;
procedure EditingColumn(aCol: Integer; Ok: boolean);
procedure EditorCancelEditing;
procedure EditorDoGetValue; override;
function EditorCanAcceptKey(const ch: TUTF8Char): boolean; override;
function EditorIsReadOnly: boolean; override;
procedure EditorTextChanged(const aCol,aRow: Integer; const aText:string); override;
procedure EndLayout;
function FieldIndexFromGridColumn(AGridCol: Integer): Integer;
function FirstGridColumn: Integer; override;
function GetBufferCount: integer; virtual;
function GetCellHintText(aCol, aRow: Integer): String; override;
function GetDefaultColumnAlignment(Column: Integer): TAlignment; override;
function GetDefaultColumnWidth(Column: Integer): Integer; override;
function GetDefaultColumnReadOnly(Column: Integer): boolean; override;
function GetDefaultColumnTitle(Column: Integer): string; override;
function GetDefaultRowHeight: integer; override;
function GetDsFieldFromGridColumn(Column: Integer): TField;
function GetEditMask(aCol, aRow: Longint): string; override;
function GetEditText(aCol, aRow: Longint): string; override;
function GetFieldFromGridColumn(Column: Integer): TField;
function GetGridColumnFromField(F: TField): Integer;
procedure GetImageForCheckBox(const aCol, aRow: Integer;
CheckBoxView: TCheckBoxState; var ImageList: TCustomImageList;
var ImageIndex: TImageIndex; var Bitmap: TBitmap); override;
function GetIsCellSelected(aCol, aRow: Integer): boolean; override;
function GetIsCellTitle(aCol,aRow: Integer): boolean; override;
procedure GetSelectedState(AState: TGridDrawState; out IsSelected:boolean); override;
function GetSmoothScroll(Which: Integer): Boolean; override;
function GetTruncCellHintText(aCol, aRow: Integer): string; override;
function GridCanModify: boolean;
procedure GetSBVisibility(out HsbVisible,VsbVisible:boolean);override;
procedure GetSBRanges(const HsbVisible,VsbVisible: boolean;
out HsbRange,VsbRange,HsbPage,VsbPage,HsbPos,VsbPos:Integer); override;
procedure HeaderClick(IsColumn: Boolean; index: Integer); override;
procedure HeaderSized(IsColumn: Boolean; Index: Integer); override;
function IsColumnVisible(aCol: Integer): boolean;
function IsValidChar(AField: TField; AChar: TUTF8Char): boolean;
procedure KeyDown(var Key : Word; Shift : TShiftState); override;
procedure LinkActive(Value: Boolean); virtual;
procedure LayoutChanged; virtual;
procedure Loaded; override;
procedure LoadGridOptions(cfg: TXMLConfig; Version: Integer); override;
procedure MoveSelection; override;
function MouseButtonAllowed(Button: TMouseButton): boolean; override;
procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure PrepareCanvas(aCol,aRow: Integer; aState:TGridDrawState); override;
procedure PrepareCellHints(aCol,aRow: Integer); override;
procedure RemoveAutomaticColumns;
procedure SaveGridOptions(Cfg: TXMLConfig); override;
procedure SelectEditor; override;
procedure SetEditText(ACol, ARow: Longint; const Value: string); override;
procedure SetFixedCols(const AValue: Integer); override;
procedure UnprepareCellHints; override;
procedure UpdateActive; virtual;
procedure UpdateAutoSizeColumns;
procedure UpdateData; virtual;
function UpdateGridCounts: Integer;
procedure WMVScroll(var Message : TLMVScroll); message LM_VScroll;
procedure WndProc(var TheMessage : TLMessage); override;
property Columns: TDBGridColumns read GetColumns write SetColumns;
property FixedRowsExtra: Integer read FFixedRowsExtra write SetFixedRowsExtra;
property GridStatus: TDBGridStatus read FGridStatus write FGridStatus;
property Datalink: TComponentDataLink read FDatalink;
property Options: TDBGridOptions read FOptions write SetOptions default
[dgColumnResize, dgColumnMove, dgTitles, dgIndicator, dgRowLines,
dgColLines, dgConfirmDelete, dgCancelOnExit, dgTabs, dgEditing,
dgAlwaysShowSelection];
property OptionsExtra: TDBGridExtraOptions read FExtraOptions
write SetExtraOptions default [dgeAutoColumns, dgeCheckboxColumn];
property ReadOnly: Boolean read FReadOnly write FReadOnly default false;
property SelectedRows: TBookmarkList read FSelectedRows;
property OnCellClick: TDBGridClickEvent read FOnCellClick write FOnCellClick;
property OnColEnter: TNotifyEvent read FOnColEnter write FOnColEnter;
property OnColExit: TNotifyEvent read FOnColExit write FOnColExit;
property OnColumnMoved: TMovedEvent read FOnColumnMoved write FOnColumnMoved;
property OnColumnSized: TNotifyEvent read FOnColumnSized write FOnColumnSized;
property OnDrawColumnCell: TDrawColumnCellEvent read FOnDrawColumnCell write FOnDrawColumnCell;
property OnDrawColumnTitle: TDrawColumnCellEvent read FOnDrawColumnTitle write FOnDrawColumnTitle;
property OnFieldEditMask: TGetDbEditMaskEvent read FOnFieldEditMask write FOnFieldEditMask;
property OnGetCellHint: TDbGridCellHintEvent read FOnGetCellHint write FOnGetCellHint;
property OnPrepareCanvas: TPrepareDbGridCanvasEvent read FOnPrepareCanvas write FOnPrepareCanvas;
property OnSelectEditor: TDbGridSelEditorEvent read FOnSelectEditor write FOnSelectEditor;
property OnTitleClick: TDBGridClickEvent read FOnTitleClick write FOnTitleClick;
property OnUserCheckboxBitmap: TDbGridCheckboxBitmapEvent read FOnCheckboxBitmap write FOnCheckboxBitmap;
property OnUserCheckboxState: TDbGridCheckboxStateEvent read FOnCheckboxState write FOnCheckboxState;
property OnRowMoved: TMovedEvent read FOnRowMoved write SetRowMoved;
public
constructor Create(AOwner: TComponent); override;
procedure AutoAdjustColumns; override;
procedure InitiateAction; override;
procedure DefaultDrawColumnCell(const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
function EditorByStyle(Style: TColumnButtonStyle): TWinControl; override;
procedure ResetColWidths;
destructor Destroy; override;
function MouseToRecordOffset(const x,y: Integer; out Column: TColumn; out RecordOffset: Integer): TGridZone;
function ExecuteAction(AAction: TBasicAction): Boolean; override;
function UpdateAction(AAction: TBasicAction): Boolean; override;
procedure SaveToFile(FileName: string); override;
procedure SaveToStream(AStream: TStream); override;
procedure LoadFromFile(FileName: string); override;
procedure LoadFromStream(AStream: TStream); override;
property AllowOutboundEvents;
property SelectedField: TField read GetCurrentField write SetCurrentField;
property SelectedIndex: Integer read GetSelectedIndex write SetSelectedIndex;
property SelectedColumn: TColumn read GetCurrentColumn;
property SelectedFieldRect: TRect read GetSelectedFieldRect;
property LastColumn: TColumn read GetLastColumn;
property FirstColumn: TColumn read GetFirstColumn;
property DataSource: TDataSource read GetDataSource write SetDataSource;
end;
TDBGrid=class(TCustomDBGrid)
public
property BorderColor;
property Canvas;
property DefaultTextStyle;
property EditorBorderStyle;
property EditorMode;
property ExtendedColSizing;
property FastEditing;
property FocusColor;
property FocusRectVisible;
property GridLineColor;
property GridLineStyle;
property InplaceEditor;
property SelectedColor;
property SelectedRows;
property OnRowMoved;
published
property Align;
property AlternateColor;
property Anchors;
property AutoAdvance default aaRightDown;
property AutoEdit;
property AutoFillColumns;
property BiDiMode;
property BorderSpacing;
property BorderStyle;
property CellHintPriority;
property Color;
property ColRowDraggingCursor;
property ColRowDragIndicatorColor;
property ColSizingCursor;
property Columns; // stored false;
property Constraints;
property DataSource;
property DefaultDrawing;
property DefaultRowHeight;
property DoubleBuffered;
property DragCursor;
//property DragKind;
property DragMode;
property Enabled;
property FixedColor;
property FixedCols;
property FixedHotColor;
property Flat;
property Font;
property HeaderHotZones;
property HeaderPushZones;
//property ImeMode;
//property ImeName;
property Options;
property Options2;
property OptionsExtra;
property ParentBiDiMode;
property ParentColor default false;
property ParentDoubleBuffered;
property ParentFont;
//property ParentShowHint;
property PopupMenu;
property ReadOnly;
property Scrollbars default ssBoth;
property ShowHint;
property TabAdvance;
property TabOrder;
property TabStop;
property TitleFont;
property TitleImageList;
property TitleStyle;
property UseXORFeatures;
property Visible;
property OnCellClick;
property OnColEnter;
property OnColExit;
property OnColumnMoved;
property OnColumnSized;
property OnContextPopup;
property OnDrawColumnCell;
property OnDrawColumnTitle;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEditButtonClick;
property OnEditingDone;
//property OnEndDock;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnFieldEditMask;
property OnGetCellHint;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseMove;
property OnMouseUp;
property OnMouseWheel;
property OnMouseWheelDown;
property OnMouseWheelUp;
property OnPrepareCanvas;
property OnSelectEditor;
//property OnStartDock;
property OnStartDrag;
property OnTitleClick;
property OnUserCheckboxBitmap;
property OnUserCheckboxImage;
property OnUserCheckboxState;
property OnUTF8KeyPress;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Data Controls',[TDBGrid]);
end;
function CalcCanvasCharWidth(Canvas:TCanvas): integer;
begin
{$ifdef dbgDBGridExtra}
DebugLnEnter('CalcCanvasCharWidth INIT');
{$endif}
if Canvas.HandleAllocated then
result := Canvas.TextWidth('MX') div 2
else
result := 8;
{$ifdef dbgDBGridExtra}
DebugLnExit('CalcCanvasCharWidth DONE result=%d', [result]);
{$endif}
end;
function CalcColumnFieldWidth(Canvas: TCanvas; hasTitle: boolean;
aTitle: String; aTitleFont: TFont; Field: TField): Integer;
var
aCharWidth, aTitleWidth: Integer;
aFont: TFont;
UseTitleFont: boolean;
begin
{$ifdef dbgDBGridExtra}
DebugLnEnter('CalcColumnFieldWidth INIT');
{$endif}
if (Field=nil) or (Field.DisplayWidth=0) then
Result := DEFCOLWIDTH
else begin
aCharWidth := CalcCanvasCharWidth(Canvas);
aTitleWidth := UTF8Length(aTitle);
if Field.DisplayWidth > aTitleWidth then
result := aCharWidth * Field.DisplayWidth
else
result := aCharWidth * aTitleWidth;
if HasTitle then begin
UseTitleFont :=
(Canvas.Font.Size<>aTitleFont.Size) or
(Canvas.Font.Style<>aTitleFont.Style) or
(Canvas.Font.CharSet<>aTitleFont.CharSet) or
(Canvas.Font.Name<>aTitleFont.Name);
if UseTitleFont then begin
aFont := TFont.Create;
aFont.Assign(Canvas.Font);
Canvas.Font := aTitleFont;
end;
try
aCharWidth := Canvas.TextWidth(ATitle)+6;
if aCharWidth>Result then
Result := aCharWidth;
finally
if UseTitleFont then begin
Canvas.Font := aFont;
aFont.Free;
end;
end;
end; // if HasTitle ...
end; // if (Field=nil) or (Field.DisplayWidth=0)
{$ifdef dbgDBGridExtra}
DebugLnExit('CalcColumnFieldWidth DONE result=%d', [result]);
{$endif}
end;
var
LookupTmpSetActive: Boolean;
LookupBookMark: TBookmark;
procedure LookupGetBookMark(ALookupField: TField);
begin
{$ifdef dbgDBGrid}
DebugLn('LookupGetBookMark');
{$endif}
LookupTmpSetActive := not ALookupField.LookupDataSet.Active;
if LookupTmpSetActive then
ALookupField.LookupDataSet.Active := True
else
begin
LookupBookMark := ALookupField.LookupDataSet.GetBookmark;
ALookupField.LookupDataSet.DisableControls;
end;
end;
procedure LookupGotoBookMark(ALookupField: TField);
begin
{$ifdef dbgDBGrid}
DebugLn('LookupGotoBookMark');
{$endif}
if LookupTmpSetActive then
begin
ALookupField.LookupDataSet.Active := False;
LookupTmpSetActive := False;
end
else
try
ALookupField.LookupDataSet.GotoBookmark(LookupBookMark);
ALookupField.LookupDataSet.FreeBookmark(LookupBookMark);
finally
ALookupField.LookupDataSet.EnableControls;
end;
end;
{ TBookmarkedRecordEnumerator }
constructor TBookmarkedRecordEnumerator.Create(bookList: TBookmarkList;
aGrid: TCustomDbGrid; anOptions: TBookmarkedRecordEnumeratorOptions);
begin
inherited Create;
fBookmarkList := bookList;
fBookmarkIndex := -1;
fDataset := aGrid.Datasource.dataset;
fOptions := anOptions;
end;
destructor TBookmarkedRecordEnumerator.Destroy;
begin
if breRestoreCurrent in fOptions then begin
if fDataset.BookmarkValid(fBook) then
fDataset.GotoBookmark(fBook);
fDataset.FreeBookmark(fBook);
end;
if breDisableDataset in fOptions then
fDataset.EnableControls;
inherited Destroy;
end;
function TBookmarkedRecordEnumerator.MoveNext: boolean;
begin
inc(fBookmarkIndex);
if fBookmarkIndex=0 then begin
if breDisableDataset in fOptions then
fDataset.DisableControls;
if breRestoreCurrent in fOptions then
fBook := fDataset.GetBookmark;
end;
result := fBookmarkIndex<fBookmarkList.Count;
if result then begin
fCurrent := fBookmarkList[fBookmarkIndex];
if fDataset.BookmarkValid(fCurrent) then
fDataSet.GotoBookmark(fCurrent)
else if breStopOnInvalidBookmark in fOptions then
result := false;
end;
end;
function TBookmarkedRecordEnumerator.GetEnumerator: TBookmarkedRecordEnumerator;
begin
result := self;
end;
{ TCustomDBGrid }
procedure TCustomDBGrid.OnRecordChanged(Field: TField);
var
c: Integer;
begin
{$ifdef dbgDBGrid}
DbgOut(ClassName,'.OnRecordChanged(Field=');
if Field=nil then DebugLn('nil)')
else DebugLn(Field.FieldName,')');
{$endif}
if Field=nil then
UpdateActive
else begin
c := GetGridColumnFromField(Field);
if c>0 then begin
if EditorMode and (Field=SelectedField) then
EditorDoSetValue
else
InvalidateCell(C, Row)
end else
UpdateActive;
end;
end;
function TCustomDBGrid.GetDataSource: TDataSource;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.GetDataSource', [ClassName]);
{$endif}
Result:= FDataLink.DataSource;
end;
function TCustomDBGrid.GetFirstColumn: TColumn;
var
i: Integer;
begin
i := ColumnIndexFromGridColumn(GetFirstVisibleColumn);
if i>=0 then
Result := Columns[i]
else
Result := nil;
end;
function TCustomDBGrid.GetLastColumn: TColumn;
var
i: Integer;
begin
i := ColumnIndexFromGridColumn(GetLastVisibleColumn);
if i>=0 then
Result := Columns[i]
else
Result := nil;
end;
function TCustomDBGrid.GetRecordCount: Integer;
begin
{$ifdef dbgDBGrid}
DebugLnEnter('%s.GetRecordCount INIT', [ClassName]);
{$endif}
result := FDataLink.DataSet.RecordCount;
{$ifdef dbgDBGrid}
DebugLnExit('%s.GetRecordCount DONE RecordCount=%d', [ClassName, result]);
{$endif}
end;
function TCustomDBGrid.GetSelectedFieldRect: TRect;
begin
result := CellRect(Col,Row);
end;
function TCustomDBGrid.GetSelectedIndex: Integer;
begin
if Columns.Enabled then
Result := ColumnIndexFromGridColumn( Col )
else
Result := FieldIndexFromGridColumn( Col );
end;
procedure TCustomDBGrid.EmptyGrid;
var
OldFixedCols, OldFixedRows: Integer;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.EmptyGrid', [ClassName]);
{$endif}
OldFixedCols := FixedCols;
OldFixedRows := FixedRows;
Clear;
RowCount := OldFixedRows + 1;
ColCount := OldFixedCols + 1;
if dgIndicator in Options then
ColWidths[0]:=Scale96ToFont(DEFINDICATORCOLWIDTH);
end;
procedure TCustomDBGrid.DoHeaderClick(Index: Integer);
var
Column: TColumn;
begin
if Assigned(OnTitleClick) then begin
Column := TColumn(ColumnFromGridColumn(Index));
if Column <> nil then
OnTitleClick(Column);
end;
end;
function TCustomDBGrid.GetColumns: TDBGridColumns;
begin
result := TDBGridColumns( inherited Columns );
end;
procedure TCustomDBGrid.InvalidateSizes;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.InvalidateSizes', [ClassName]);
{$endif}
GridFlags := GridFlags + [gfVisualChange];
end;
function TCustomDBGrid.GetCurrentColumn: TColumn;
begin
if Columns.Enabled then
Result := TColumn(Columns[SelectedIndex])
else
Result := nil;
end;
function TCustomDBGrid.GetCurrentField: TField;
begin
result := GetFieldFromGridColumn( Col );
end;
procedure TCustomDBGrid.OnDataSetChanged(aDataSet: TDataSet);
begin
{$ifdef dbgDBGrid}
DebugLnEnter('%s.OnDataSetChanged INIT name=%s aDataSet=%s',
[ClassName,name,dbgsname(ADataset)]);
{$endif}
if not (gsStartEditing in FGridStatus) then begin
GridFlags := GridFlags + [gfEditingDone];
if EditorMode then
EditorMode := False;
GridFlags := GridFlags - [gfEditingDone];
LayoutChanged;
end;
UpdateActive;
if not (gsStartEditing in FGridStatus) then begin
SelectEditor;
if (dgAlwaysShowEditor in Options) and not EditorMode then
EditorMode := true;
end;
{$ifdef dbgDBGrid}
DebugLnExit('%s.OnDataSetChanged DONE name=%s aDataSet=%s',
[ClassName,name,dbgsname(ADataset)]);
{$endif}
end;
procedure TCustomDBGrid.OnDataSetOpen(aDataSet: TDataSet);
begin
{$ifdef dbgDBGrid}
DebugLnEnter('%s.OnDataSetOpen INIT', [ClassName]);
{$endif}
RenewColWidths;
LinkActive(True);
UpdateActive;
SelectEditor;
{$ifdef dbgDBGrid}
DebugLnExit('%s.OnDataSetOpen DONE', [ClassName]);
{$endif}
end;
procedure TCustomDBGrid.OnDataSetClose(aDataSet: TDataSet);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.OnDataSetClose', [ClassName]);
{$endif}
LinkActive(False);
end;
procedure TCustomDBGrid.OnEditingChanged(aDataSet: TDataSet);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.OnEditingChanged', [ClassName]);
if aDataSet<>nil then begin
DebugLn(['Editing=', dsEdit = aDataSet.State]);
DebugLn(['Inserting=',dsInsert = aDataSet.State]);
end else
DebugLn('Dataset=nil');
{$endif}
FDataLink.Modified := False;
UpdateActive;
end;
procedure TCustomDBGrid.OnInvalidDataSet(aDataSet: TDataSet);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.OnInvalidDataSet', [ClassName]);
{$endif}
LinkActive(False);
end;
procedure TCustomDBGrid.OnInvalidDataSource(aDataSet: TDataset);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.OnInvalidDataSource', [ClassName]);
{$endif}
LinkActive(False);
end;
procedure TCustomDBGrid.OnFocusControl(aField: TFieldRef);
var
aIndex: Integer;
begin
if CanFocus and (aField<>nil) and (aField^<>nil) then begin
aIndex := GetGridColumnFromField(aField^);
if aIndex>=FirstGridColumn then begin
SelectedField := aField^;
aField^ := nil;
SetFocus;
end;
end;
end;
procedure TCustomDBGrid.OnLayoutChanged(aDataSet: TDataSet);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.OnLayoutChanged', [ClassName]);
{$endif}
LayoutChanged;
end;
procedure TCustomDBGrid.OnNewDataSet(aDataSet: TDataset);
begin
{$ifdef dbgDBGrid}
DebugLnEnter('%s.OnNewDataSet INIT', [ClassName]);
{$endif}
RenewColWidths;
LinkActive(True);
UpdateActive;
SelectEditor;
{$ifdef dbgDBGrid}
DebugLnExit('%s.OnNewDataSet DONE', [ClassName]);
{$endif}
end;
procedure TCustomDBGrid.OnDataSetScrolled(aDataSet: TDataSet; Distance: Integer
);
var
OldEditorMode: boolean;
OldRow: Integer;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.OnDataSetScrolled Distance=%d ds.RecordCount=%d',[ClassName, Distance, aDataSet.RecordCount]);
{$endif}
UpdateScrollBarRange;
// todo: Use a fast interface method to scroll a rectangular section of window
// if distance=+, Row[Distance] to Row[RowCount-2] UP
// if distance=-, Row[FixedRows+1] to Row[RowCount+Distance] DOWN
OldEditorMode := EditorMode;
if OldEditorMode then
EditorMode := False;
if Distance<>0 then begin
OldRow := Row;
Row := FixedRows + FDataLink.ActiveRecord;
if OldRow=Row then // if OldRow<>NewRow SelectEditor will be called by MoveExtend
SelectEditor; // if OldRow=NewRow we need to manually call SelectEditor
Invalidate;
end else
UpdateActive;
if OldEditorMode and (dgAlwaysShowEditor in Options) then
EditorMode := True;
end;
procedure TCustomDBGrid.OnUpdateData(aDataSet: TDataSet);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.OnUpdateData', [ClassName]);
{$endif}
UpdateData;
end;
procedure TCustomDBGrid.SetColumns(const AValue: TDBGridColumns);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.SetColumns', [ClassName]);
{$endif}
inherited Columns := TGridColumns(AValue);
end;
{
procedure TCustomDBGrid.ReadColumns(Reader: TReader);
begin
Columns.Clear;
Reader.ReadValue;
Reader.ReadCollection(Columns);
end;
procedure TCustomDBGrid.SetColumns(const AValue: TDBGridColumns);
begin
Columns.Assign(AValue);
end;
}
procedure TCustomDBGrid.SetCurrentField(const AValue: TField);
var
i: Integer;
begin
if Avalue<>SelectedField then begin
i := GetGridColumnFromField( AValue );
if (i>=FirstGridColumn) and (i>=FixedCols) then
Col := i;
end;
end;
procedure TCustomDBGrid.SetDataSource(const AValue: TDataSource);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.SetDataSource', [ClassName]);
{$endif}
if AValue = FDatalink.Datasource then Exit;
RenewColWidths;
FDataLink.DataSource := AValue;
UpdateActive;
end;
procedure TCustomDBGrid.SetExtraOptions(const AValue: TDBGridExtraOptions);
var
OldOptions: TDBGridExtraOptions;
function IsOptionChanged(Op: TDBGridExtraOption): boolean;
begin
result := ((op in OldOptions) and not (op in AValue)) or
(not (op in OldOptions) and (op in AValue));
end;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.SetExtraOptions', [ClassName]);
{$endif}
if FExtraOptions=AValue then exit;
OldOptions := FExtraOptions;
FExtraOptions := AValue;
if IsOptionChanged(dgeCheckboxColumn) then
Invalidate;
if IsOptionChanged(dgeAutoColumns) then begin
if dgeAutoColumns in aValue then
AddAutomaticColumns
else if TDBGridColumns(Columns).HasAutomaticColumns then
RemoveAutomaticColumns;
UpdateActive;
end;
end;
procedure TCustomDBGrid.SetFixedRowsExtra(AValue: Integer);
begin
if FFixedRowsExtra = AValue then Exit;
FFixedRowsExtra := AValue;
LayoutChanged;
end;
procedure TCustomDBGrid.SetOptions(const AValue: TDBGridOptions);
var
OldOptions: TGridOptions;
ChangedOptions: TDbGridOptions;
MultiSel: boolean;
begin
{$ifdef dbgDBGrid}
DebugLnEnter('%s.SetOptions INIT', [ClassName]);
{$endif}
if FOptions<>AValue then begin
MultiSel := dgMultiSelect in FOptions;
ChangedOptions := (FOptions-AValue) + (AValue-FOptions);
FOptions:=AValue;
OldOptions := inherited Options;
if dgRowSelect in FOptions then
FOptions := FOptions - [dgEditing, dgAlwaysShowEditor, dgRowHighlight];
BeginLayout;
if dgRowLines in fOptions then
Include(OldOptions, goHorzLine)
else
Exclude(OldOptions, goHorzLine);
if dgColLines in fOptions then
Include(OldOptions, goVertLine)
else
Exclude(OldOptions, goVertLine);
if dgColumnResize in fOptions then
Include(OldOptions, goColSizing)
else
Exclude(OldOptions, goColSizing);
if dgColumnMove in fOptions then
Include(OldOptions, goColMoving)
else
Exclude(OldOptions, goColMoving);
if dgAlwaysShowEditor in FOptions then
Include(OldOptions, goAlwaysShowEditor)
else
Exclude(OldOptions, goAlwaysShowEditor);
if dgRowSelect in FOptions then
Include(OldOptions, goRowSelect)
else
Exclude(OldOptions, goRowSelect);
if dgEditing in FOptions then
Include(OldOptions, goEditing)
else
Exclude(OldOptions, goediting);
if dgTabs in FOptions then
Include(OldOptions, goTabs)
else
Exclude(OldOptions, goTabs);
if dgHeaderHotTracking in FOptions then
Include(OldOptions, goHeaderHotTracking)
else
Exclude(OldOptions, goHeaderHotTracking);
if dgHeaderPushedLook in FOptions then
Include(OldOptions, goHeaderPushedLook)
else
Exclude(OldOptions, goHeaderPushedLook);
if dgCellHints in FOptions then
Include(OldOptions, goCellHints)
else
Exclude(OldOptions, goCellHints);
if dgTruncCellHints in FOptions then
Include(OldOptions, goTruncCellHints)
else
Exclude(OldOptions, goTruncCellHints);
if dgCellEllipsis in FOptions then
Include(OldOptions, goCellEllipsis)
else
Exclude(OldOptions, goCellEllipsis);
if dgRowHighlight in FOptions then
Include(OldOptions, goRowHighlight)
else
Exclude(OldOptions, goRowHighlight);
if dgDblClickAutoSize in FOptions then
Include(OldOptions, goDblClickAutoSize)
else
Exclude(OldOptions, goDblClickAutoSize);
if (dgIndicator in ChangedOptions) then begin
if (dgIndicator in FOptions) then
FixedCols := FixedCols + 1
else
FixedCols := Max(FixedCols - 1, 0);
end;
if (dgTitles in ChangedOptions) then begin
if dgTitles in FOptions then
FixedRows := FixedRows + 1
else
FixedRows := Max(FixedRows - 1, 0);
end;
if (dgAutoSizeColumns in ChangedOptions) then begin
Exclude(FGridStatus, gsAutoSized);
end;
if dgThumbTracking in ChangedOptions then begin
if dgThumbTracking in FOptions then
Include(OldOptions, goThumbTracking)
else
Exclude(OldOptions, goThumbTracking);
end;
inherited Options := OldOptions;
if MultiSel and not (dgMultiSelect in FOptions) then begin
FSelectedRows.Clear;
end;
EndLayout;
end;
{$ifdef dbgDBGrid}
DebugLnExit('%s.SetOptions DONE', [ClassName]);
{$endif}
end;
procedure TCustomDBGrid.SetRowMoved(AValue: TMovedEvent);
begin
if FOnRowMoved = AValue then
Exit;
FOnRowMoved := AValue;
if assigned(OnRowMoved) then
inherited Options := inherited Options + [goRowMoving]
else
inherited Options := inherited Options - [goRowMoving];
end;
procedure TCustomDBGrid.SetSelectedIndex(const AValue: Integer);
begin
Col := FirstGridColumn + AValue;
end;
procedure TCustomDBGrid.UpdateBufferCount;
var
BCount: Integer;
begin
{$ifdef dbgDBGrid}
DebugLnEnter('%s.UpdateBufferCount INIT', [ClassName]);
{$endif}
if FDataLink.Active then begin
BCount := GetBufferCount;
if BCount<1 then
BCount := 1;
FDataLink.BufferCount:= BCount;
end;
{$ifdef dbgDBGrid}
DebugLnExit('%s.UpdateBufferCount DONE BufferCount=%d', [ClassName, FDataLink.BufferCount]);
{$endif}
end;
procedure TCustomDBGrid.UpdateData;
var
selField,edField: TField;
LookupKeyValues: Variant;
begin
// get Editor text and update field content
if not UpdatingData and (FEditingColumn>-1) and FDatalink.Editing then begin
SelField := SelectedField;
edField := GetFieldFromGridColumn(FEditingColumn);
if (edField<>nil) and (edField = SelField) then begin
{$ifdef dbgDBGrid}
DebugLnEnter('%s.UpdateData INIT Field[%s(%s)]=%s',
[ClassName, edField.Fieldname ,edField.AsString, FTempText]);
{$endif}
StartUpdating;
try
edField.Text := FTempText;
if edField.FieldKind = fkLookup then
begin
LookupKeyValues := Null;
if edField.LookupCache then
LookupKeyValues := edField.LookupList.FirstKeyByValue(FTempText)
else
begin
LookupGetBookMark(edField);
try
if edField.LookupDataSet.Locate(edField.LookupResultField,
VarArrayOf([FTempText]), []) then
LookupKeyValues :=
edField.LookupDataSet.FieldValues[edField.LookupKeyFields];
finally
LookupGotoBookMark(edField);
end;
end;
edField.DataSet.FieldValues[edField.KeyFields] := LookupKeyValues;
end;
finally
EndUpdating;
end;
EditingColumn(FEditingColumn, False);
{$ifdef dbgDBGrid}
DebugLnExit('%s.UpdateData DONE Field=%s',[ClassName, edField.ASString]);
{$endif}
end;
end;
end;
{$ifdef dbgDBGrid}
function SBCodeToStr(Code: Integer): String;
begin
Case Code of
SB_LINEUP : result := 'SB_LINEUP';
SB_LINEDOWN: result := 'SB_LINEDOWN';
SB_PAGEUP: result := 'SB_PAGEUP';
SB_PAGEDOWN: result := 'SB_PAGEDOWN';
SB_THUMBTRACK: result := 'SB_THUMBTRACK';
SB_THUMBPOSITION: result := 'SB_THUMBPOSITION';
SB_ENDSCROLL: result := 'SB_SCROLLEND';
SB_TOP: result := 'SB_TOP';
SB_BOTTOM: result := 'SB_BOTTOM';
else result :=IntToStr(Code)+ ' -> ?';
end;
end;
{$endif}
procedure TCustomDBGrid.WMVScroll(var Message: TLMVScroll);
var
IsSeq: boolean;
aPos, aRange, aPage: Integer;
DeltaRec: integer;
function MaxPos: Integer;
begin
if IsSeq then
result := GetRecordCount - 1
else
result := 4;
end;
procedure DsMoveBy(Delta: Integer);
begin
FDataLink.MoveBy(Delta);
GetScrollbarParams(aRange, aPage, aPos);
end;
procedure DsGoto(BOF: boolean);
begin
if BOF then FDatalink.DataSet.First
else FDataLink.DataSet.Last;
GetScrollbarParams(aRange, aPage, aPos);
end;
function DsPos: boolean;
var
oldMaxPos: Integer;
begin
result := false;
aPos := Message.Pos;
if aPos=FOldPosition then begin
result := true;
exit;
end;
oldMaxPos := MaxPos;
if aPos>=oldMaxPos then
dsGoto(False)
else if aPos<=0 then
dsGoto(True)
else if IsSeq then begin
FDatalink.DataSet.RecNo := aPos + 1;
{$IFDEF MSWINDOWS}
// Workaround for scrollbar range not being updated
// probably only needed under windows, issue 33799
if oldMaxPos<>MaxPos then begin
ScrollBarShow(SB_VERT, false);
ScrollBarShow(SB_VERT, true);
end;
{$ENDIF}
end
else begin
DeltaRec := Message.Pos - FOldPosition;
if DeltaRec=0 then begin
result := true;
exit
end
else if DeltaRec<-1 then
DsMoveBy(-VisibleRowCount)
else if DeltaRec>1 then
DsMoveBy(VisibleRowCount)
else
DsMoveBy(DeltaRec);
end;
end;
begin
if not FDatalink.Active then exit;
{$ifdef dbgDBGrid}
DebugLnEnter('%s.WMVScroll INIT Code=%s Position=%s OldPos=%s',
[ClassName, SbCodeToStr(Message.ScrollCode), dbgs(Message.Pos), Dbgs(FOldPosition)]);
{$endif}
aPos := 0;
IsSeq := FDatalink.DataSet.IsSequenced and not FDataLink.DataSet.Filtered;
case Message.ScrollCode of
SB_TOP:
DsGoto(True);
SB_BOTTOM:
DsGoto(False);
SB_PAGEUP:
DsMoveBy(-VisibleRowCount);
SB_LINEUP:
DsMoveBy(-1);
SB_LINEDOWN:
DsMoveBy(1);
SB_PAGEDOWN:
DsMoveBy(VisibleRowCount);
SB_THUMBPOSITION:
if DsPos then
exit;
SB_THUMBTRACK:
if dgThumbTracking in Options then begin
if not (FDatalink.DataSet.IsSequenced) or DsPos then begin
{$ifdef dbgDBGrid}
DebugLnExit('%s.WMVScroll EXIT: SB_THUMBTRACK: DsPos or not sequenced', [ClassName]);
{$endif}
exit;
end;
end else begin
{$ifdef dbgDBGrid}
DebugLnExit('%s.WMVScroll EXIT: SB_THUMBTRACK: not using dgThumbTracking', [ClassName]);
{$endif}
Exit;
end;
else begin
{$ifdef dbgDBGrid}
DebugLnExit('%s.WMVScroll EXIT: invalid ScrollCode: %d', [ClassName, message.ScrollCode]);
{$endif}
Exit;
end;
end;
ScrollBarPosition(SB_VERT, aPos);
FOldPosition:=aPos;
if EditorMode then
RestoreEditor;
{$ifdef dbgDBGrid}
DebugLnExit('%s.WMVScroll DONE Diff=%s FinalPos=%s', [ClassName, dbgs(DeltaRec), dbgs(aPos)]);
{$endif}
end;
procedure TCustomDBGrid.WndProc(var TheMessage: TLMessage);
begin
if (TheMessage.Msg=LM_SETFOCUS) and (gsUpdatingData in FGridStatus) then begin
{$ifdef dbgGrid}DebugLn('%s.LM_SETFOCUS while updating', [ClassName]);{$endif}
if EditorMode then begin
LCLIntf.SetFocus(Editor.Handle);
EditorSelectAll;
end;
exit;
end;
inherited WndProc(TheMessage);
end;
function TCustomDBGrid.DefaultFieldColWidth(F: TField): Integer;
begin
if not HandleAllocated or (F=nil) then
result:=DefaultColWidth
else begin
if F.DisplayWidth = 0 then
if Canvas.HandleAllocated then
result := Canvas.TextWidth( F.DisplayName ) + 3
else
Result := DefaultColWidth
else
result := F.DisplayWidth * CalcCanvasCharWidth(Canvas);
end;
end;
function TCustomDBGrid.GetColumnCount: Integer;
var
i: integer;
F: TField;
begin
result := 0;
if Columns.Enabled then
result := Columns.VisibleCount
else
if (dgeAutoColumns in OptionsExtra) and FDataLink.Active then
for i:=0 to FDataLink.DataSet.FieldCount-1 do begin
F:= FDataLink.DataSet.Fields[i];
if (F<>nil) and F.Visible then
Inc(Result);
end;
end;
// Get the visible field (from dataset fields) that corresponds to given column
function TCustomDBGrid.GetDsFieldFromGridColumn(Column: Integer): TField;
var
i: Integer;
begin
i := FieldIndexFromGridColumn( Column );
if i>=0 then
Result := FDataLink.DataSet.Fields[i]
else
Result := nil;
end;
function TCustomDBGrid.FirstGridColumn: Integer;
begin
if (dgIndicator in Options) then
Result := 1
else
Result := 0;
end;
procedure TCustomDBGrid.PrepareCellHints(aCol, aRow: Integer);
begin
if not DataLink.Active then Exit;
FSavedRecord := DataLink.ActiveRecord;
DataLink.ActiveRecord := ARow - FixedRows;
end;
procedure TCustomDBGrid.UnprepareCellHints;
begin
if not DataLink.Active then Exit;
DataLink.ActiveRecord := FSavedRecord;
end;
function TCustomDBGrid.GetCellHintText(aCol, aRow: Integer): String;
var
C: TColumn;
begin
Result := '';
if (ARow < FixedRows) then
exit;
if Assigned(FOnGetCellHint) then begin
C := ColumnFromGridColumn(ACol) as TColumn;
FOnGetCellHint(self, C, Result);
end;
end;
function TCustomDBGrid.GetTruncCellHintText(aCol, aRow: Integer): string;
var
F: TField;
C: TColumn;
begin
Result := '';
if ARow < FixedRows then
exit;
F := GetFieldFromGridColumn(ACol);
if (F <> nil) then
if CheckDisplayMemo(f) then
result := F.AsString
else
if (F.DataType <> ftBlob) then
Result := F.DisplayText
else
Result := '(blob)';
// pass to OnGetCellHint() only if chpTruncOnly
if Assigned(OnGetCellHint) and (CellHintPriority = chpTruncOnly) then begin
C := ColumnFromGridColumn(ACol) as TColumn;
FOnGetCellHint(self, C, Result);
end;
end;
// obtain the field either from a Db column or directly from dataset fields
function TCustomDBGrid.GetFieldFromGridColumn(Column: Integer): TField;
var
i: integer;
begin
if Columns.Enabled then begin
i := ColumnIndexFromGridColumn( Column );
if i>=0 then
result := TDBGridColumns(Columns)[i].FField
else
result := nil;
end else
result := GetDsFieldFromGridColumn(Column);
end;
// obtain the corresponding grid column for the given field
function TCustomDBGrid.GetGridColumnFromField(F: TField): Integer;
var
i: Integer;
begin
result := -1;
for i:=FirstGridColumn to ColCount-1 do begin
if GetFieldFromGridColumn(i) = F then begin
result := i;
break;
end;
end;
end;
procedure TCustomDBGrid.GetImageForCheckBox(const aCol, aRow: Integer;
CheckBoxView: TCheckBoxState; var ImageList: TCustomImageList;
var ImageIndex: TImageIndex; var Bitmap: TBitmap);
begin
inherited GetImageForCheckBox(aCol, aRow, CheckBoxView, ImageList, ImageIndex, Bitmap);
if Assigned(OnUserCheckboxBitmap) then
OnUserCheckboxBitmap(Self, CheckBoxView, Bitmap);
end;
// obtain the visible field index corresponding to the grid column index
function TCustomDBGrid.FieldIndexFromGridColumn(AGridCol: Integer): Integer;
var
i: Integer;
Column: TColumn;
begin
result := -1;
if not FDatalink.Active then
exit;
if Columns.Enabled then begin
Column := TColumn(ColumnFromGridColumn(AGridCol));
if (Column<>nil) and (Column.Field<>nil) and Column.Field.Visible then
Result := FDatalink.Dataset.Fields.IndexOf(Column.Field)
end else begin
AGridCol := AGridCol - FirstGridColumn;
i := 0;
while (AGridCol>=0) and (i<FDatalink.DataSet.FieldCount) do begin
if FDatalink.Fields[i].Visible then begin
Dec(AGridCol);
if AGridCol<0 then begin
Result := i;
break;
end;
end;
inc(i);
end;
end;
end;
function TCustomDBGrid.GetBufferCount: integer;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.GetBufferCount', [ClassName]);
{$endif}
Result := ClientHeight div DefaultRowHeight;
if dgTitles in Options then
Dec(Result, FixedRows);
end;
procedure TCustomDBGrid.UpdateGridColumnSizes;
var
i: Integer;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.UpdateGridColumnSizes', [ClassName]);
{$endif}
if FDefaultColWidths then begin
if dgIndicator in Options then
ColWidths[0]:=Scale96ToFont(12);
if NeedAutoSizeColumns then
UpdateAutoSizeColumns;
end;
end;
procedure TCustomDBGrid.UpdateScrollbarRange;
var
aRange, aPage, aPos: Integer;
ScrollInfo: TScrollInfo;
begin
if not HandleAllocated then exit;
{$ifdef dbgDBGrid}
DebugLnEnter('%s.UpdateScrollbarRange INIT', [ClassName]);
{$endif}
GetScrollBarParams(aRange, aPage, aPos);
if (ScrollBars in [ssBoth, ssVertical])
or ((Scrollbars in [ssAutoVertical, ssAutoBoth]) and (aRange>aPage)) then
begin
FillChar(ScrollInfo, SizeOf(ScrollInfo), 0);
ScrollInfo.cbSize := SizeOf(ScrollInfo);
{TODO: try to move this out}
{$ifdef WINDOWS}
ScrollInfo.fMask := SIF_ALL or SIF_DISABLENOSCROLL;
ScrollInfo.ntrackPos := 0;
{$else}
ScrollInfo.fMask := SIF_ALL or SIF_UPDATEPOLICY;
//ScrollInfo.ntrackPos := SB_POLICY_CONTINUOUS;
ScrollInfo.ntrackPos := SB_POLICY_DISCONTINUOUS;
{$endif}
ScrollInfo.nMin := 0;
ScrollInfo.nMax := aRange;
ScrollInfo.nPos := Min(aPos,aRange-aPage);
ScrollInfo.nPage := aPage;
SetScrollInfo(Handle, SB_VERT, ScrollInfo, True);
end;
FOldPosition := aPos;
{$ifdef dbgDBGrid}
DebugLnExit('%s.UpdateScrollBarRange DONE Handle=%d aRange=%d aPage=%d aPos=%d',
[ClassName, Handle, aRange, aPage, aPos]);
{$endif}
end;
procedure TCustomDBGrid.DoLayoutChanged;
begin
if csDestroying in ComponentState then
exit;
{$ifdef dbgDBGrid}DebugLnEnter('%s.doLayoutChanged INIT', [ClassName]);{$endif}
BeginUpdate;
if UpdateGridCounts=0 then
EmptyGrid;
EndUpdate;
UpdateScrollbarRange;
{$ifdef dbgDBGrid}DebugLnExit('%s.doLayoutChanged DONE', [ClassName]);{$endif}
end;
{
procedure TCustomDBGrid.WriteColumns(Writer: TWriter);
begin
if Columns.IsDefault then
Writer.WriteCollection(nil)
else
Writer.WriteCollection(Columns);
end;
}
procedure TCustomDBGrid.RestoreEditor;
begin
if EditorMode then begin
EditorMode := False;
EditorMode := True;
end;
end;
function TCustomDBGrid.ISEOF: boolean;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.IsEOF', [ClassName]);
{$endif}
Result := FDatalink.Active and FDatalink.DataSet.EOF;
end;
function TCustomDBGrid.ValidDataSet: boolean;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.ValidDataSet', [ClassName]);
{$endif}
Result := FDatalink.Active And (FDatalink.DataSet<>nil)
end;
function TCustomDBGrid.InsertCancelable: boolean;
begin
with FDatalink.DataSet do
Result := (State=dsInsert) and not (Modified or FDataLink.FModified);
end;
procedure TCustomDBGrid.StartUpdating;
begin
if not UpdatingData then begin
{$ifdef dbgDBGrid}DebugLn('%s.StartUpdating', [ClassName]);{$endif}
Include(FGridStatus, gsUpdatingData);
FOldControlStyle := ControlStyle;
ControlStyle := ControlStyle + [csActionClient];
LockEditor;
end
else
{$ifdef dbgDBGrid}DebugLn('WARNING: multiple calls to StartUpdating');{$endif}
end;
procedure TCustomDBGrid.EndUpdating;
begin
{$ifdef dbgDBGrid}DebugLn('%s.EndUpdating', [ClassName]);{$endif}
Exclude(FGridStatus, gsUpdatingData);
ControlStyle := FOldControlStyle;
UnLockEditor;
if csActionClient in ControlStyle then
DebugLn('WARNING: still got csActionClient');
end;
function TCustomDBGrid.UpdatingData: boolean;
begin
result := gsUpdatingData in FGridStatus;
end;
procedure TCustomDBGrid.AddAutomaticColumns;
var
i: Integer;
F: TField;
begin
// add as many columns as there are fields in the dataset
// do this only at runtime.
if (csDesigning in ComponentState) or not FDatalink.Active or
(gsRemovingAutoColumns in FGridStatus) or (gsLoadingGrid in FGridStatus) or
not (dgeAutoColumns in OptionsExtra)
then
exit;
Include(FGridStatus, gsAddingAutoColumns);
try
for i:=0 to FDataLink.DataSet.FieldCount-1 do begin
F:= FDataLink.DataSet.Fields[i];
if TDBGridColumns(Columns).ColumnFromField(F) <> nil then
// this field is already in the collection. This could only happen
// if AddAutomaticColumns was called out of LayoutChanged.
// to avoid duplicate columns skip this field.
continue;
if (F<>nil) then begin
with TDBGridColumns(Columns).Add do begin
FIsAutomaticColumn := True;
Field := F;
Visible := F.Visible;
end;
end;
end;
// honor the field.index
TDBGridColumns(Columns).ResetColumnsOrder(coFieldIndexOrder);
finally
Exclude(FGridStatus, gsAddingAutoColumns);
end;
end;
procedure TCustomDBGrid.AssignTo(Dest: TPersistent);
begin
if Dest is TCustomDbGrid then begin
// TODO
end else
inherited AssignTo(Dest);
end;
procedure TCustomDBGrid.AutoAdjustColumn(aCol: Integer);
var
DatalinkActive: Boolean;
CurActiveRecord: Integer;
tmpCanvas: TCanvas;
begin
BeginLayout;
DatalinkActive := FDatalink.Active;
if DatalinkActive then
CurActiveRecord := FDatalink.ActiveRecord;
tmpCanvas := GetWorkingCanvas(Canvas);
try
InternalAutoSizeColumn(aCol,tmpCanvas,DatalinkActive);
finally
if TmpCanvas<>Canvas then
FreeWorkingCanvas(tmpCanvas);
if DatalinkActive then
FDatalink.ActiveRecord := CurActiveRecord;
EndLayout;
end;
end;
procedure TCustomDBGrid.UpdateAutoSizeColumns;
var
ACol: Integer;
DatalinkActive: boolean;
CurActiveRecord: Integer;
tmpCanvas: TCanvas;
begin
if gsAutoSized in GridStatus then
exit;
BeginLayout;
DatalinkActive := FDatalink.Active;
if DatalinkActive then
CurActiveRecord := FDatalink.ActiveRecord;
tmpCanvas := GetWorkingCanvas(Canvas);
try
for aCol:=FixedCols to ColCount-1 do
InternalAutoSizeColumn(ACol,tmpCanvas,DatalinkActive);
finally
if TmpCanvas<>Canvas then
FreeWorkingCanvas(tmpCanvas);
if DatalinkActive then
FDatalink.ActiveRecord := CurActiveRecord;
include(FGridStatus, gsAutoSized);
EndLayout;
end;
end;
procedure TCustomDBGrid.SwapCheckBox;
var
SelField: TField;
TempColumn: TColumn;
begin
if not GridCanModify then
exit;
SelField := SelectedField;
TempColumn := TColumn(ColumnFromGridColumn(Col));
if (SelField<>nil) and (TempColumn<>nil) and not TempColumn.ReadOnly and
FDatalink.Edit then
begin
if SelField.DataType=ftBoolean then
SelField.AsBoolean := not SelField.AsBoolean
else
begin
if TempColumn.ValueChecked=SelField.AsString then
SelField.AsString := TempColumn.ValueUnchecked
else
SelField.AsString := TempColumn.ValueChecked;
end;
end;
end;
procedure TCustomDBGrid.ToggleSelectedRow;
begin
SelectRecord(not FSelectedRows.CurrentRowSelected);
end;
procedure TCustomDBGrid.LinkActive(Value: Boolean);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.LinkActive', [ClassName]);
{$endif}
if not Value then begin
FSelectedRows.Clear;
RemoveAutomaticColumns;
end;
LayoutChanged;
end;
procedure TCustomDBGrid.LayoutChanged;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.LayoutChanged', [ClassName]);
{$endif}
if csDestroying in ComponentState then
exit;
if FLayoutChangedCount=0 then begin
BeginLayout;
if Columns.Count>0 then
TDBGridColumns(Columns).LinkFields
else if not FDataLink.Active then
FDataLink.BufferCount := 0
else
AddAutomaticColumns;
EndLayout;
end;
end;
procedure TCustomDBGrid.Loaded;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.Loaded', [ClassName]);
{$endif}
LayoutChanged;
inherited Loaded;
end;
procedure TCustomDBGrid.LoadGridOptions(cfg: TXMLConfig; Version: Integer);
var
Opt: TDBGridOptions;
Path: string;
procedure GetValue(optStr:string; aOpt:TDBGridOption);
begin
if Cfg.GetValue(Path+OptStr+'/value', False) then Opt:=Opt+[aOpt];
end;
begin
Opt:=[];
Path:='grid/design/options/';
GetValue('dgEditing', dgEditing);
GetValue('dgTitles', dgTitles);
GetValue('dgIndicator', dgIndicator);
GetValue('dgColumnResize', dgColumnResize);
GetValue('dgColumnMove', dgColumnMove);
GetValue('dgColLines', dgColLines);
GetValue('dgRowLines', dgRowLines);
GetValue('dgTabs', dgTabs);
GetValue('dgAlwaysShowEditor', dgAlwaysShowEditor);
GetValue('dgRowSelect', dgRowSelect);
GetValue('dgAlwaysShowSelection', dgAlwaysShowSelection);
GetValue('dgConfirmDelete', dgConfirmDelete);
GetValue('dgCancelOnExit', dgCancelOnExit);
GetValue('dgMultiselect', dgMultiselect);
GetValue('dgHeaderHotTracking', dgHeaderHotTracking);
GetValue('dgHeaderPushedLook', dgHeaderPushedLook);
GetValue('dgPersistentMultiSelect', dgPersistentMultiSelect);
GetValue('dgAutoSizeColumns', dgAutoSizeColumns);
GetValue('dgAnyButtonCanSelect', dgAnyButtonCanSelect);
GetValue('dgDisableDelete', dgDisableDelete);
GetValue('dgDisableInsert', dgDisableInsert);
GetValue('dgCellHints', dgCellHints);
GetValue('dgTruncCellHints', dgTruncCellHints);
GetValue('dgCellEllipsis', dgCellEllipsis);
GetValue('dgRowHighlight', dgRowHighlight);
GetValue('dgThumbTracking', dgThumbTracking);
Options:=Opt;
end;
type
TProtFields=class(TFields)
end;
procedure TCustomDBGrid.ColRowMoved(IsColumn: Boolean; FromIndex,
ToIndex: Integer);
var
F: TField;
begin
if IsColumn then begin
if Columns.Enabled then
inherited ColRowMoved(IsColumn, FromIndex, ToIndex)
else if FDatalink.Active and (FDataLink.DataSet<>nil) then begin
F := GetDsFieldFromGridColumn(FromIndex);
if F<>nil then begin
TProtFields(FDatalink.DataSet.Fields).SetFieldIndex( F, ToIndex - FirstGridColumn );
end;
end;
if Assigned(OnColumnMoved) then
OnColumnMoved(Self, FromIndex, ToIndex);
end
else if Assigned(OnRowMoved) then
OnRowMoved(Self, FromIndex, ToIndex);
end;
function TCustomDBGrid.ColumnEditorStyle(aCol: Integer; F: TField): TColumnButtonStyle;
var
gridcol: TGridColumn;
begin
result := cbsAuto;
gridcol := ColumnFromGridColumn(aCol);
if Columns.Enabled and assigned(gridcol) then
result := gridcol.ButtonStyle;
result := DefaultEditorStyle(result, F);
end;
function TCustomDBGrid.CreateColumns: TGridColumns;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.CreateColumns', [ClassName]);
{$endif}
result := TDBGridColumns.Create(Self, TColumn);
end;
procedure TCustomDBGrid.CreateWnd;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.CreateWnd', [ClassName]);
{$endif}
inherited CreateWnd;
LayoutChanged;
if Scrollbars in [ssBoth, ssVertical, ssAutoBoth, ssAutoVertical] then
ScrollBarShow(SB_VERT, True);
end;
procedure TCustomDBGrid.DefineProperties(Filer: TFiler);
{
function HasColumns: boolean;
var
C: TGridColumns;
begin
if Filer.Ancestor <> nil then
C := TCustomGrid(Filer.Ancestor).Columns
else
C := Columns;
if C<>nil then
result := not C.IsDefault
else
result := false;
end;
}
begin
// simply avoid to call TCustomGrid.DefineProperties method
// which defines ColWidths,Rowheights,Cells
//Filer.DefineProperty('Columns', @ReadColumns, @WriteColumns, HasColumns);
end;
procedure TCustomDBGrid.DefaultDrawCell(aCol, aRow: Integer; aRect: TRect;
aState: TGridDrawState);
var
S: string;
F: TField;
cbs: TColumnButtonStyle;
begin
DrawCellBackground(aCol, aRow, aRect, aState);
if gdFixed in aState then
DrawFixedText(aCol, aRow, aRect, aState)
else
if not FDrawingEmptyDataset then begin
F := GetFieldFromGridColumn(aCol);
cbs := ColumnEditorStyle(aCol, F);
case cbs of
cbsCheckBoxColumn:
DrawCheckBoxBitmaps(aCol, aRect, F);
else
begin
if cbs=cbsButtonColumn then
DrawButtonCell(aCol, aRow, aRect, aState);
{$ifdef dbggridpaint}
DbgOut(' Col=%d',[ACol]);
{$endif}
if F<>nil then begin
{$ifdef dbgGridPaint}
DbgOut(' Field=%s',[F.FieldName]);
{$endif}
if CheckDisplayMemo(F) then
S := F.AsString
else
S := F.DisplayText;
end else
S := '';
{$ifdef dbggridpaint}
DbgOut(' Value=%s ',[S]);
{$endif}
DrawCellText(aCol,aRow,aRect,aState,S);
end;
end;
end;
end;
function TCustomDBGrid.DefaultEditorStyle(const Style: TColumnButtonStyle;
const F: TField): TColumnButtonStyle;
begin
result := Style;
if (Result=cbsAuto) and (F<>nil) then
case F.DataType of
ftBoolean: Result := cbsCheckboxColumn;
end;
if (result = cbsCheckBoxColumn) and not (dgeCheckboxColumn in FExtraOptions) then
Result := cbsAuto;
end;
procedure TCustomDBGrid.DoCopyToClipboard;
var
F: TField;
begin
// copy current field to clipboard
if not FDatalink.Active then
exit;
F := GetFieldFromGridColumn(Col);
if F<>nil then
Clipboard.AsText := F.AsString;
end;
procedure TCustomDBGrid.DoOnChangeBounds;
begin
BeginUpdate;
inherited DoOnChangeBounds;
if HandleAllocated then
LayoutChanged;
EndUpdate;
end;
procedure TCustomDBGrid.DoPrepareCanvas(aCol, aRow: Integer;
aState: TGridDrawState);
var
DataCol: Integer;
IsSelected: boolean;
begin
if (ARow>=FixedRows) then begin
if not DefaultDrawing then begin
GetSelectedState(aState, IsSelected);
if IsSelected then begin
Canvas.Brush.Color := SelectedColor;
Canvas.Font.Color := clHighlightText;
end;
end;
if Assigned(OnPrepareCanvas) then begin
DataCol := ColumnIndexFromGridColumn(aCol);
if DataCol>=0 then
OnPrepareCanvas(Self, DataCol, TColumn(Columns[DataCol]), aState);
end;
end;
end;
procedure TCustomDBGrid.DoLoadColumn(sender: TCustomGrid; aColumn: TGridColumn;
aColIndex: Integer; aCfg: TXMLConfig; aVersion: Integer; aPath: string);
var
c: TColumn;
s: string;
begin
c:=TColumn(aColumn);
s := aCfg.GetValue(aPath + '/fieldname/value', '');
if s<>'' then
c.FieldName := s;
s := aCfg.GetValue(aPath + '/displayformat/value', '');
if s<>'' then
c.DisplayFormat := s;
inherited DoLoadColumn(sender, aColumn, aColIndex, aCfg, aVersion, aPath);
end;
procedure TCustomDBGrid.DoSaveColumn(sender: TCustomGrid; aColumn: TGridColumn;
aColIndex: Integer; aCfg: TXMLConfig; aVersion: Integer; aPath: string);
var
c: TColumn;
begin
c:=TColumn(aColumn);
aCfg.SetValue(aPath + '/fieldname/value', c.FieldName);
aCfg.SetValue(aPath + '/displayformat/value', c.DisplayFormat);
inherited DoSaveColumn(sender, aColumn, aColIndex, aCfg, aVersion, aPath);
end;
procedure TCustomDBGrid.BeforeMoveSelection(const DCol,DRow: Integer);
begin
{$ifdef dbgDBGrid}DebugLnEnter('%s.BeforeMoveSelection INIT', [ClassName]);{$endif}
inherited BeforeMoveSelection(DCol, DRow);
if DCol<>Col then begin
if assigned(OnColExit) then
OnColExit(Self);
FColEnterPending:=True;
end;
{$ifdef dbgDBGrid}DebugLnExit('%s.BeforeMoveSelection DONE', [ClassName]);{$endif}
end;
procedure TCustomDBGrid.HeaderClick(IsColumn: Boolean; index: Integer);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.HeaderClick', [ClassName]);
{$endif}
if IsColumn then
DoHeaderClick(Index);
end;
procedure TCustomDBGrid.KeyDown(var Key: Word; Shift: TShiftState);
type
TOperation=(opMoveBy,opCancel,opAppend,opInsert,opDelete);
var
DeltaCol,DeltaRow: Integer;
preSelIndex, posSelIndex: Integer;
procedure DoOnKeyDown;
begin
{$ifdef dbgGrid}DebugLnEnter('DoOnKeyDown INIT');{$endif}
if Assigned(OnKeyDown) then
OnKeyDown(Self, Key, Shift);
{$ifdef dbgGrid}DebugLnExit('DoOnKeyDown DONE');{$endif}
end;
{$ifdef dbgGrid}
function OperToStr(AOper: TOperation): string;
begin
case AOper of
opMoveBy: result := 'opMoveBy';
opCancel: result := 'opCancel';
opAppend: result := 'opAppend';
opInsert: result := 'opInsert';
opDelete: result := 'opDelete';
end;
end;
{$endif}
procedure DoOperation(AOper: TOperation; Arg: Integer = 0);
begin
{$ifdef dbgGrid}DebugLnEnter('KeyDown.DoOperation(%s,%d) INIT',[OperToStr(AOper),arg]);{$endif}
GridFlags := GridFlags + [gfEditingDone];
case AOper of
opMoveBy:
FDatalink.MoveBy(Arg);
opCancel:
begin
if EditorMode then
EditorCancelEditing;
FDatalink.Dataset.Cancel;
end;
opAppend:
FDatalink.Dataset.Append;
opInsert:
FDatalink.Dataset.Insert;
opDelete:
FDatalink.Dataset.Delete;
end;
GridFlags := GridFlags - [gfEditingDone];
{$ifdef dbgGrid}DebugLnExit('KeyDown.DoOperation(%s,%d) DONE',[OperToStr(AOper),arg]);{$endif}
end;
procedure SelectNext(const AStart,ADown:Boolean);
var
N, curActiveRecord: Integer;
CurBookmark: TBookmark;
begin
if dgPersistentMultiSelect in Options then
exit;
if (ssShift in Shift) then begin
if dgMultiSelect in Options then begin
curBookmark := FDatalink.DataSet.GetBookmark;
try
if AStart then preSelIndex := FSelectedRows.IndexOf(curBookmark)
else posSelIndex := FSelectedRows.IndexOf(curBookmark);
if not AStart then begin
FSelectedRows.CurrentRowSelected := true;
// deal with selection of previous (not prior) record
curActiveRecord := FDatalink.ActiveRecord;
try
if ADown then FDatalink.ActiveRecord := FDatalink.ActiveRecord - 1
else FDatalink.ActiveRecord := FDatalink.ActiveRecord + 1;
if (preSelIndex>=0) and (posSelIndex>=0) then begin
if preSelIndex<>posSelIndex then
FSelectedRows.CurrentRowSelected := false
end else
FSelectedRows.CurrentRowSelected := true;
finally
FDatalink.ActiveRecord := curActiveRecord;
end;
end;
finally
FDatalink.DataSet.FreeBookmark(CurBookmark);
end;
end;
end else
ClearSelection(true);
end;
function doVKDown: boolean;
begin
{$ifdef dbgGrid}DebugLnEnter('DoVKDown INIT');{$endif}
if InsertCancelable then
begin
if IsEOF then
result:=true
else begin
doOperation(opCancel);
result := false;
end;
end else begin
result:=false;
SelectNext(true,true);
doOperation(opMoveBy, 1);
if GridCanModify and FDataLink.EOF then begin
if not (dgDisableInsert in Options) then
doOperation(opAppend);
end else
SelectNext(false,true);
end;
{$ifdef dbgGrid}DebugLnExit('DoVKDown DONE');{$endif}
end;
function DoVKUP: boolean;
begin
{$ifdef dbgGrid}DebugLnEnter('DoVKUP INIT');{$endif}
if InsertCancelable then
doOperation(opCancel)
else begin
SelectNext(true, false);
doOperation(opMoveBy, -1);
SelectNext(false, false);
end;
result := FDatalink.DataSet.BOF;
{$ifdef dbgGrid}DebugLnExit('DoVKUP DONE');{$endif}
end;
procedure MoveSel(AReset: boolean);
var
ACol: Integer;
begin
if (DeltaCol<>0) or (DeltaRow<>0) then begin
if DeltaRow > 0 then begin
if doVKDown then
//DeltaCol:=0; // tochk: strict? already in EOF, don't change column
end else
if DeltaRow < 0 then begin
if doVKUP then
//DeltaCol:=0; // tochk: strict? already in BOF, don't change column
end;
GridFlags := GridFlags + [gfEditingDone];
if (DeltaCol<>0) then
if Col + DeltaCol < FixedCols then
Col := FixedCols
else if Col + DeltaCol >= ColCount then
Col := ColCount - 1
else
begin
ACol := Col + DeltaCol;
if ColWidths[ACol] > 0 then
Col := ACol
else
if DeltaCol < 0 then
Col := GetFirstVisibleColumn
else
Col := GetLastVisibleColumn;
end;
GridFlags := GridFlags - [gfEditingDone];
end else
if AReset then
ResetEditor;
end;
begin
{$ifdef dbgGrid}DebugLnEnter('%s.KeyDown %s INIT Key=%d',[ClassName, Name,Key]);{$endif}
case Key of
VK_TAB:
begin
doOnKeyDown;
if (Key<>0) and ValidDataset then begin
if (dgTabs in Options) then begin
if ((ssShift in shift) and
(Col<=GetFirstVisibleColumn) and (Row<=GetFirstVisibleRow)) then begin
if EditorKey then
GridFlags := GridFlags + [gfRevEditorTab];
{$ifdef dbgGrid}DebugLnExit('%s.KeyDown Exit: Tab: Shift',[ClassName]);{$endif}
exit;
end;
GetDeltaMoveNext(ssShift in Shift, DeltaCol, DeltaRow, TabAdvance);
if (not (ssShift in Shift)) and (Row>=GetLastVisibleRow) and
(DeltaRow>0) and (Col=GetLastVisibleColumn) and
(FDatalink.Editing or not GridCanModify) then begin
{$ifdef dbgGrid}DebugLnExit('%s.KeyDown Exit: Tab: not shift',[ClassName]);{$endif}
exit;
end;
MoveSel(false);
Key := 0;
end;
end;
end;
VK_RETURN:
begin
doOnKeyDown;
if (Key<>0) and ValidDataset then begin
key:=0;
if (dgEditing in Options) and not EditorMode then
EditorMode:=true
else begin
GetDeltaMoveNext(ssShift in Shift, DeltaCol, DeltaRow, AutoAdvance);
MoveSel(True);
end;
end;
end;
VK_DELETE:
begin
doOnKeyDown;
if (Key<>0) and (ssCtrl in Shift) and GridCanModify and
(not (dgDisableDelete in Options)) and
not FDataLink.DataSet.IsEmpty then begin
if not (dgConfirmDelete in Options) or
(MessageDlg(rsDeleteRecord, mtConfirmation, mbOKCancel, 0 )<>mrCancel)
then begin
doOperation(opDelete);
key := 0;
end;
end;
end;
VK_DOWN:
begin
DoOnKeyDown;
if (Key<>0) and ValidDataset then begin
doVKDown;
Key := 0;
end;
end;
VK_UP:
begin
doOnKeyDown;
if (Key<>0) and ValidDataset then begin
doVKUp;
key := 0;
end;
end;
VK_NEXT:
begin
doOnKeyDown;
if (Key<>0) and ValidDataset then begin
doOperation(opMoveBy, VisibleRowCount);
ClearSelection(true);
Key := 0;
end;
end;
VK_PRIOR:
begin
doOnKeyDown;
if (Key<>0) and ValidDataset then begin
doOperation(opMoveBy, -VisibleRowCount);
ClearSelection(true);
key := 0;
end;
end;
VK_ESCAPE:
begin
doOnKeyDown;
if (Key<>0) and ValidDataset then begin
if EditorMode then begin
EditorCancelEditing;
if FDatalink.Active and not FDatalink.Dataset.Modified then
FDatalink.Modified := False;
Key := 0;
end else
if FDataLink.Active then
doOperation(opCancel);
end;
end;
VK_INSERT:
begin
doOnKeyDown;
if Key<>0 then
if not (dgDisableInsert in Options) and GridCanModify then begin
doOperation(opInsert);
Key:=0;
end;
end;
VK_HOME:
begin
doOnKeyDown;
if Key<>0 then begin
if FDatalink.Active then begin
GridFlags := GridFlags + [gfEditingDone];
if ssCTRL in Shift then
FDataLink.DataSet.First
else
MoveNextSelectable(False, FixedCols, Row);
GridFlags := GridFlags - [gfEditingDone];
ClearSelection(true);
Key:=0;
end;
end;
end;
VK_END:
begin
doOnKeyDown;
if Key<>0 then begin
if FDatalink.Active then begin
GridFlags := GridFlags + [gfEditingDone];
if ssCTRL in shift then
FDatalink.DataSet.Last
else begin
DeltaCol := GetLastVisibleColumn;
if DeltaCol>=0 then
MoveNextSelectable(False, DeltaCol, Row);
end;
GridFlags := GridFlags - [gfEditingDone];
ClearSelection(true);
Key:=0;
end;
end;
end;
VK_SPACE:
begin
doOnKeyDown;
if (Key<>0) and ValidDataset then begin
if ColumnEditorStyle(Col, SelectedField) = cbsCheckboxColumn then begin
SwapCheckBox;
Key:=0;
end;
end;
end;
VK_MULTIPLY:
begin
doOnKeyDown;
if (Key<>0) and ValidDataset and (ssCtrl in Shift) then
ToggleSelectedRow;
end;
else
inherited KeyDown(Key, Shift);
end;
{$ifdef dbgGrid}DebugLnExit('%s.KeyDown DONE Key= %d',[ClassName, Key]);{$endif}
end;
procedure TCustomDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
var
Gz: TGridZone;
P: TPoint;
aoe: Boolean;
procedure doMouseDown;
begin
if not Focused and not(csNoFocus in ControlStyle) then
SetFocus;
if assigned(OnMouseDown) then
OnMouseDown(Self, Button, Shift, X, Y);
end;
procedure doInherited;
begin
inherited MouseDown(Button, Shift, X, Y);
end;
procedure doMoveBy;
begin
{$ifdef dbgGrid}DebugLnEnter('%s.MouseDown MoveBy INIT', [ClassName]); {$endif}
FDatalink.MoveBy(P.Y - Row);
{$ifdef dbgGrid}DebugLnExit('%s.MouseDown MoveBy DONE', [ClassName]); {$endif}
end;
procedure doMoveToColumn;
begin
{$ifdef dbgGrid}DebugLnEnter('%s.MouseDown MoveToCol INIT Col=%d', [ClassName, P.X]); {$endif}
Col := P.X;
{$ifdef dbgGrid}DebugLnExit('%s.MouseDown MoveToCol DONE', [ClassName]); {$endif}
end;
procedure DoCancel;
begin
{$ifdef dbgGrid}DebugLnEnter('%s.MouseDown Dataset.CANCEL INIT', [ClassName]);{$endif}
if EditorMode then
EditorCancelEditing;
FDatalink.Dataset.cancel;
{$ifdef dbgGrid}DebugLnExit('%s.MouseDown Dataset.CANCEL DONE', [ClassName]);{$endif}
end;
procedure DoAcceptValue;
begin
if EditorMode and FDatalink.FModified then
EditorMode := False;
end;
begin
if (csDesigning in componentState) {or not GCache.ValidGrid }then begin
{$ifdef dbgDBGrid}DebugLn('%s.MouseDown - checkDesigning', [ClassName]);{$endif}
exit;
end;
if UpdatingData then begin
{$ifdef dbgDBGrid}DebugLn('%s.MouseDown - UpdatingData', [ClassName]);{$endif}
exit;
end;
if not MouseButtonAllowed(Button) then begin
{$ifdef dbgDBGrid}DebugLn('%s.MouseDown - no mouse allowed', [ClassName]);{$endif}
doInherited;
exit;
end;
{$ifdef dbgGrid}DebugLnEnter('%s.MouseDown INIT', [ClassName]); {$endif}
Gz:=MouseToGridZone(X,Y);
CacheMouseDown(X,Y);
case Gz of
gzInvalid:
begin
if (cursor=crHSplit) and (dgColumnResize in Options) then begin
// DBGrid normally doesn't allow outbound events and this is one of them
// make GCache.HotGridZone valid for inherited mousedown. Issue #0034032
aoe := AllowOutboundEvents;
AllowOutboundEvents := true;
inherited MouseMove(shift, x, y);
AllowOutBoundEvents := aoe;
doInherited;
end else
doMouseDown;
end;
gzFixedCells, gzFixedCols:
doInherited;
else
begin
P:=MouseToCell(Point(X,Y));
if Gz=gzFixedRows then
P.X := Col;
if P.Y=Row then begin // The current active row was clicked again.
//doAcceptValue;
if ssCtrl in Shift then begin
doMouseDown;
// Don't unselect the row if Right-click was for PopupMenu.
if (Button<>mbRight) or (PopupMenu=Nil) then
ToggleSelectedRow;
end
else begin
if Button=mbLeft then
ClearSelection(true);
if gz=gzFixedRows then begin
fGridState:=gsRowMoving;
ResetLastMove;
doMouseDown;
end
else
doInherited;
end;
end else begin
doMouseDown;
if ValidDataSet then begin
if InsertCancelable and IsEOF then
doCancel;
doMoveBy;
if IsMouseOverCellButton(X, Y) then
StartPushCell;
end;
if ssCtrl in Shift then
ToggleSelectedRow
else begin
if (Button=mbLeft) or (dgAnyButtonCanSelect in Options) then
ClearSelection(true);
// Select row before popupmenu
if (Button=mbRight) and Assigned(PopupMenu) and not FSelectedRows.CurrentRowSelected then
ToggleSelectedRow;
doMoveToColumn;
end;
end;
end;
end;
{$ifdef dbgGrid}DebugLnExit('%s.MouseDown DONE', [ClassName]); {$endif}
end;
procedure TCustomDBGrid.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
if (fGridState=gsSelecting) and not Dragging then begin
if Assigned(OnMouseMove) then
OnMouseMove(Self, Shift, x, y);
exit;
end else
inherited MouseMove(Shift, X, Y);
end;
procedure TCustomDBGrid.PrepareCanvas(aCol, aRow: Integer;
aState: TGridDrawState);
begin
inherited PrepareCanvas(aCol, aRow, aState);
if gdFixed in aState then begin
if gdHot in aState then
Canvas.Brush.Color := FixedHotColor
else
Canvas.Brush.Color := GetColumnColor(aCol, gdFixed in AState);
end;
if (not FDatalink.Active) and ((gdSelected in aState) or (gdFocused in aState)) then
Canvas.Brush.Color := Self.Color;
end;
procedure TCustomDBGrid.RemoveAutomaticColumns;
begin
if not (csDesigning in ComponentState) then
TDBGridColumns(Columns).RemoveAutoColumns;
end;
procedure TCustomDBGrid.SaveGridOptions(Cfg: TXMLConfig);
var
Path: string;
begin
Path:='grid/design/options/';
Cfg.SetValue(Path+'dgEditing/value', dgEditing in Options);
Cfg.SetValue(Path+'dgTitles/value', dgTitles in Options);
Cfg.SetValue(Path+'dgIndicator/value', dgIndicator in Options);
Cfg.SetValue(Path+'dgColumnResize/value', dgColumnResize in Options);
Cfg.SetValue(Path+'dgColumnMove/value', dgColumnMove in Options);
Cfg.SetValue(Path+'dgColLines/value', dgColLines in Options);
Cfg.SetValue(Path+'dgRowLines/value', dgRowLines in Options);
Cfg.SetValue(Path+'dgTabs/value', dgTabs in Options);
Cfg.SetValue(Path+'dgAlwaysShowEditor/value', dgAlwaysShowEditor in Options);
Cfg.SetValue(Path+'dgRowSelect/value', dgRowSelect in Options);
Cfg.SetValue(Path+'dgAlwaysShowSelection/value', dgAlwaysShowSelection in Options);
Cfg.SetValue(Path+'dgConfirmDelete/value', dgConfirmDelete in Options);
Cfg.SetValue(Path+'dgCancelOnExit/value', dgCancelOnExit in Options);
Cfg.SetValue(Path+'dgMultiselect/value', dgMultiselect in Options);
Cfg.SetValue(Path+'dgHeaderHotTracking/value', dgHeaderHotTracking in Options);
Cfg.SetValue(Path+'dgHeaderPushedLook/value', dgHeaderPushedLook in Options);
Cfg.SetValue(Path+'dgPersistentMultiSelect/value', dgPersistentMultiSelect in Options);
cfg.SetValue(Path+'dgAutoSizeColumns/value', dgAutoSizeColumns in Options);
cfg.SetValue(Path+'dgAnyButtonCanSelect/value', dgAnyButtonCanSelect in Options);
Cfg.SetValue(Path+'dgDisableDelete/value', dgDisableDelete in Options);
Cfg.SetValue(Path+'dgDisableInsert/value', dgDisableInsert in Options);
Cfg.SetValue(Path+'dgCellHints/value', dgCellHints in Options);
cfg.SetValue(Path+'dgTruncCellHints/value', dgTruncCellHints in Options);
Cfg.SetValue(Path+'dgCellEllipsis/value', dgCellEllipsis in Options);
Cfg.SetValue(Path+'dgRowHighlight/value', dgRowHighlight in Options);
Cfg.SetValue(Path+'dgThumbTracking/value', dgThumbTracking in Options);
end;
procedure TCustomDBGrid.SelectEditor;
var
aEditor: TWinControl;
aMaxLen: integer;
begin
{$ifdef dbgDBGrid}
DebugLnEnter('%s.SelectEditor INIT Editor=%s',[ClassName, dbgsname(editor)]);
{$endif}
if (FDatalink<>nil) and FDatalink.Active then begin
inherited SelectEditor;
if (SelectedField is TStringField) then
aMaxLen := SelectedField.Size
else
aMaxLen := 0;
if (Editor is TCustomEdit) then
TCustomEdit(Editor).MaxLength := aMaxLen
else
if (Editor is TCompositeCellEditor) then
TCompositeCellEditor(Editor).MaxLength := aMaxLen;
if Assigned(OnSelectEditor) then begin
aEditor:=Editor;
OnSelectEditor(Self, SelectedColumn, aEditor);
Editor:=aEditor;
end;
end else
Editor := nil;
{$ifdef dbgDBGrid}
DebugLnExit('%s.SelectEditor DONE Editor=%s',[ClassName, dbgsname(editor)]);
{$endif}
end;
procedure TCustomDBGrid.SetEditText(ACol, ARow: Longint; const Value: string);
begin
FTempText := Value;
end;
procedure TCustomDBGrid.SetFixedCols(const AValue: Integer);
begin
if (FixedCols=AValue) or (AValue<FirstGridColumn) then
exit;
inherited SetFixedCols(AValue);
end;
procedure TCustomDBGrid.BeginLayout;
begin
inc(FLayoutChangedCount);
end;
procedure TCustomDBGrid.EditingColumn(aCol: Integer; Ok: boolean);
begin
{$ifdef dbgDBGrid}DebugLnEnter('%s.EditingColumn INIT aCol=%d Ok=%s',
[ClassName, aCol, BoolToStr(ok, true)]); {$endif}
if Ok then begin
FEditingColumn := aCol;
FDatalink.Modified := True;
end
else
FEditingColumn := -1;
{$ifdef dbgDBGrid} DebugLnExit('%s.EditingColumn DONE', [ClassName]); {$endif}
end;
procedure TCustomDBGrid.EditorCancelEditing;
begin
EditingColumn(FEditingColumn, False); // prevents updating the value
if EditorMode then begin
EditorMode := False;
if dgAlwaysShowEditor in Options then
EditorMode := True;
end;
end;
procedure TCustomDBGrid.EditorDoGetValue;
begin
{$ifdef dbgDBGrid}DebugLnEnter('%s.EditorDoGetValue INIT', [ClassName]);{$endif}
inherited EditordoGetValue;
UpdateData;
{$ifdef dbgDBGrid}DebugLnExit('%s.EditorDoGetValue DONE', [ClassName]);{$endif}
end;
procedure TCustomDBGrid.CellClick(const aCol, aRow: Integer; const Button:TMouseButton);
begin
{$ifdef dbgGrid}DebugLn('%s.CellClick', [ClassName]); {$endif}
if Button<>mbLeft then
exit;
if (aCol>=FirstGridColumn) then begin
if (aRow>=FixedRows) then begin
if IsColumnVisible(aCol) and
(ColumnEditorStyle(ACol, SelectedField) = cbsCheckboxColumn) then begin
// react only if overriden editor is hidden
if (Editor=nil) or not EditorMode then
SwapCheckBox
end;
if Assigned(OnCellClick) then
OnCellClick(TColumn(ColumnFromGridColumn(aCol)));
end else
DoHeaderClick(aCol)
end;
end;
function TCustomDBGrid.CheckDisplayMemo(aField: TField): boolean;
begin
// note that this assumes that aField is not nil
result := (aField.DataType=ftMemo) and (dgDisplayMemoText in Options);
end;
procedure TCustomDBGrid.EndLayout;
begin
dec(FLayoutChangedCount);
if FLayoutChangedCount = 0 then
DoLayoutChanged;
end;
function TCustomDBGrid.GetDefaultColumnAlignment(Column: Integer): TAlignment;
var
F: TField;
begin
F := GetDsFieldFromGridColumn(Column);
if F<>nil then
result := F.Alignment
else
result := taLeftJustify;
end;
function TCustomDBGrid.GetDefaultColumnWidth(Column: Integer): Integer;
begin
Result := DefaultFieldColWidth(GetDsFieldFromGridColumn(Column));
end;
function TCustomDBGrid.GetDefaultColumnReadOnly(Column: Integer): boolean;
var
F: Tfield;
begin
result := true;
if not Self.ReadOnly and (FDataLink.Active and not FDatalink.ReadOnly) then begin
F := GetDsFieldFromGridColumn(Column);
result := (F=nil) or F.ReadOnly;
end;
end;
function TCustomDBGrid.GetDefaultColumnTitle(Column: Integer): string;
var
F: Tfield;
begin
F := GetDsFieldFromGridColumn(Column);
if F<>nil then
Result := F.DisplayName
else
Result := '';
end;
function TCustomDBGrid.GetDefaultRowHeight: integer;
begin
result := inherited GetDefaultRowHeight;
Dec(Result, 2); // a litle smaller for dbgrid
end;
procedure TCustomDBGrid.DoExit;
begin
{$ifdef dbgDBGrid}DebugLnEnter('%s.DoExit INIT', [ClassName]);{$endif}
if ValidDataSet and (dgCancelOnExit in Options) and
InsertCancelable then
begin
FDataLink.DataSet.Cancel;
EditingColumn(FEditingColumn, False);
end;
inherited DoExit;
{$ifdef dbgDBGrid}DebugLnExit('%s.DoExit DONE', [ClassName]);{$endif}
end;
function TCustomDBGrid.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint
): Boolean;
begin
Result := False;
if Assigned(OnMouseWheelDown) then
OnMouseWheelDown(Self, Shift, MousePos, Result);
if not Result and FDatalink.Active then begin
FDatalink.MoveBy(1);
Result := True;
end;
end;
function TCustomDBGrid.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint
): Boolean;
begin
Result := False;
if Assigned(OnMouseWheelUp) then
OnMouseWheelUp(Self, Shift, MousePos, Result);
if not Result and FDatalink.Active then begin
FDatalink.MoveBy(-1);
Result := True;
end;
end;
function TCustomDBGrid.GetEditMask(aCol, aRow: Longint): string;
var
aField: TField;
begin
Result := '';
if FDataLink.Active then begin
aField := GetFieldFromGridColumn(aCol);
if (aField<>nil) then begin
Result := aField.EditMask;
if assigned(OnFieldEditMask) then
OnFieldEditMask(Self, AField, Result);
end;
end;
end;
function TCustomDBGrid.GetEditText(aCol, aRow: Longint): string;
var
aField: TField;
begin
Result := '';
if FDataLink.Active then begin
aField := GetFieldFromGridColumn(aCol);
if aField<>nil then begin
if CheckDisplayMemo(aField) then
Result := aField.AsString
else
Result := aField.Text;
end;
end;
end;
function TCustomDBGrid.GetIsCellSelected(aCol, aRow: Integer): boolean;
begin
Result:=inherited GetIsCellSelected(aCol, aRow) or
FDrawingMultiSelRecord;
end;
function TCustomDBGrid.GetIsCellTitle(aCol, aRow: Integer): boolean;
begin
result := (FixedRows>0) and (aRow=0);
if result and Columns.Enabled then
result := (aCol>=FirstGridColumn);
end;
procedure TCustomDBGrid.GetSelectedState(AState: TGridDrawState; out
IsSelected: boolean);
begin
inherited GetSelectedState(AState, IsSelected);
if IsSelected and not Self.Focused and not(dgAlwaysShowSelection in Options) then
IsSelected := false;
end;
function TCustomDBGrid.GetSmoothScroll(Which: Integer): Boolean;
begin
if Which=SB_Vert then
Result := False
else
Result := inherited GetSmoothScroll(Which);
end;
function TCustomDBGrid.GridCanModify: boolean;
begin
result := not ReadOnly and (dgEditing in Options) and not FDataLink.ReadOnly
and FDataLink.Active and FDatalink.DataSet.CanModify;
end;
procedure TCustomDBGrid.GetSBVisibility(out HsbVisible, VsbVisible: boolean);
var
aRange,aPage,aPos: Integer;
begin
inherited GetSBVisibility(HsbVisible, VsbVisible);
VSbVisible := (ScrollBars in [ssVertical, ssBoth]);
if not VSbVisible and ScrollBarAutomatic(ssVertical) then begin
GetScrollbarParams(aRange,aPage, aPos);
if ARange>aPage then
VSbVisible:=True;
end;
end;
procedure TCustomDBGrid.GetSBRanges(const HsbVisible, VsbVisible: boolean; out
HsbRange, VsbRange, HsbPage, VsbPage, HsbPos, VsbPos: Integer);
begin
inherited GetSBRanges(HsbVisible, VsbVisible, HsbRange, VsbRange, HsbPage, VsbPage, HsbPos, VsbPos);
if VSbVisible then
GetScrollbarParams(VsbRange, VsbPage, VsbPos)
else begin
VsbRange := 0;
VsbPage := 0;
VsbPos := 0;
end;
end;
procedure TCustomDBGrid.MoveSelection;
begin
{$ifdef dbgDBGrid}DebugLnEnter('%s.MoveSelection INIT', [ClassName]);{$endif}
inherited MoveSelection;
if FColEnterPending and Assigned(OnColEnter) then begin
OnColEnter(Self);
end;
FColEnterPending:=False;
UpdateActive;
{$ifdef dbgDBGrid}DebugLnExit('%s.MoveSelection DONE', [ClassName]);{$endif}
end;
function TCustomDBGrid.MouseButtonAllowed(Button: TMouseButton): boolean;
begin
Result:= FDataLink.Active and ((Button=mbLeft) or (dgAnyButtonCanSelect in Options));
end;
procedure TCustomDBGrid.DrawAllRows;
var
CurActiveRecord: Integer;
begin
if FDataLink.Active then begin
{$ifdef dbgGridPaint}
DebugLnEnter('%s DrawAllRows INIT Link.ActiveRecord=%d, Row=%d',[Name, FDataLink.ActiveRecord, Row]);
{$endif}
CurActiveRecord:=FDataLink.ActiveRecord;
FDrawingEmptyDataset:=FDatalink.DataSet.IsEmpty;
end else
FDrawingEmptyDataset:=True;
try
inherited DrawAllRows;
finally
if FDataLink.Active then begin
FDataLink.ActiveRecord:=CurActiveRecord;
{$ifdef dbgGridPaint}
DebugLnExit('%s DrawAllRows DONE Link.ActiveRecord=%d, Row=%d',[Name, FDataLink.ActiveRecord, Row]);
{$endif}
end;
end;
end;
procedure TCustomDBGrid.DrawFocusRect(aCol, aRow: Integer; ARect: TRect);
var
DrawBits: Byte;
begin
// Draw focused cell if we have the focus
if Self.Focused and (dgAlwaysShowSelection in Options) and
FDatalink.Active and DefaultDrawing then
begin
DrawBits := BF_RECT;
if (dgRowSelect in Options) then begin
if (LeftCol>FixedCols) or (GCache.TLColOff<>0) then
DrawBits := DrawBits and not BF_LEFT;
if (GCache.VisibleGrid.Right<ColCount-1) then
DrawBits := DrawBits and not BF_RIGHT;
end;
CalcFocusRect(aRect);
DrawRubberRect(Canvas, aRect, FocusColor, DrawBits);
end;
end;
//
procedure TCustomDBGrid.DrawRow(ARow: Integer);
begin
if (ARow>=FixedRows) and FDataLink.Active then begin
//if (Arow>=FixedRows) and FCanBrowse then
FDataLink.ActiveRecord:=ARow-FixedRows;
FDrawingActiveRecord := ARow = Row;
FDrawingMultiSelRecord := (dgMultiSelect in Options) and
SelectedRows.CurrentRowSelected
end else begin
FDrawingActiveRecord := False;
FDrawingMultiSelRecord := False;
end;
{$ifdef dbgGridPaint}
DbgOut('DrawRow Row=', IntToStr(ARow), ' Act=', dbgs(FDrawingActiveRecord));
{$endif}
inherited DrawRow(ARow);
{$ifdef dbgGridPaint}
DebugLn('End Row')
{$endif}
end;
procedure TCustomDBGrid.DrawCell(aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState);
var
DataCol: Integer;
begin
PrepareCanvas(aCol, aRow, aState);
{$ifdef dbgGridPaint}
DbgOut(' ',IntToStr(aCol));
if gdSelected in aState then DbgOut('S');
if gdFocused in aState then DbgOut('*');
if gdFixed in aState then DbgOut('F');
{$endif dbgGridPaint}
if (gdFixed in aState) or DefaultDrawing then
DefaultDrawCell(aCol, aRow, aRect, aState)
else
if not DefaultDrawing then
DrawCellBackground(aCol, aRow, aRect, aState);
if not (csDesigning in ComponentState) then
begin
if (ARow>=FixedRows) and Assigned(OnDrawColumnCell) then begin
DataCol := ColumnIndexFromGridColumn(aCol);
if DataCol>=0 then
OnDrawColumnCell(Self, aRect, DataCol, TColumn(Columns[DataCol]), aState);
end;
if (ARow<FixedRows) and Assigned(OnDrawColumnTitle) then begin
DataCol := ColumnIndexFromGridColumn(aCol);
if DataCol>=0 then
OnDrawColumnTitle(Self, aRect, DataCol, TColumn(Columns[DataCol]), aState);
end;
end;
DrawCellGrid(aCol, aRow, aRect, aState);
end;
procedure TCustomDBGrid.DrawCellBackground(aCol, aRow: Integer; aRect: TRect;
aState: TGridDrawState);
begin
// background
if (gdFixed in aState) and (TitleStyle=tsNative) then
DrawThemedCell(aCol, aRow, aRect, aState)
else
Canvas.FillRect(aRect);
end;
procedure TCustomDBGrid.DrawCheckboxBitmaps(aCol: Integer; aRect: TRect;
F: TField);
var
AState: TCheckboxState;
begin
if (aCol=Col) and FDrawingActiveRecord then begin
// show checkbox only if overriden editor is hidden
if EditorMode then
exit;
end;
// by SSY
if (F<>nil) then
if F.DataType=ftBoolean then
if F.IsNull then
AState := cbGrayed
else
if F.AsBoolean then
AState := cbChecked
else
AState := cbUnChecked
else
if F.AsString=ColumnFromGridColumn(aCol).ValueChecked then
AState := cbChecked
else
if F.AsString=ColumnFromGridColumn(aCol).ValueUnChecked then
AState := cbUnChecked
else
AState := cbGrayed
else
AState := cbGrayed;
if assigned(OnUserCheckboxState) then
OnUserCheckboxState(Self, TColumn(ColumnFromGridColumn(aCol)), AState);
DrawGridCheckboxBitmaps(aCol, Row{dummy}, ARect, AState);
end;
procedure TCustomDBGrid.DrawFixedText(aCol, aRow: Integer; aRect: TRect;
aState: TGridDrawState);
function GetDatasetState: TDataSetState;
begin
if FDatalink.Active then
result := FDataLink.DataSet.State
else
result := dsInactive;
end;
begin
if (ACol=0) and (dgIndicator in Options) and FDrawingActiveRecord then begin
DrawIndicator(Canvas, aRect, GetDataSetState, FDrawingMultiSelRecord);
{$ifdef dbgGridPaint}
dbgOut('>');
{$endif}
end else
if (ACol=0) and (dgIndicator in Options) and FDrawingMultiSelRecord then
DrawIndicator(Canvas, aRect, dsCurValue{dummy}, True)
else
DrawColumnText(aCol, aRow, aRect, aState);
end;
procedure TCustomDBGrid.DrawColumnText(aCol, aRow: Integer; aRect: TRect;
aState: TGridDrawState);
var
F: TField;
s: String;
begin
if GetIsCellTitle(aCol, aRow) then
inherited DrawColumnText(aCol, aRow, aRect, aState)
else if aRow<FixedRows then
// this case is for drawing fixed rows extra, the standard dbgrid
// have nothing to draw here but it must avoid duplicate titles or
// draw some field.
else begin
F := GetFieldFromGridColumn(aCol);
if F<>nil then begin
if CheckDisplayMemo(F) then
s := F.AsString
else
s := F.DisplayText;
DrawCellText(aCol, aRow, aRect, aState, s)
end;
end;
end;
procedure TCustomDBGrid.DrawIndicator(ACanvas: TCanvas; R: TRect;
Opt: TDataSetState; MultiSel: boolean);
var
dx, dy, x, y: Integer;
procedure CenterY;
begin
y := R.Top + (R.Bottom-R.Top) div 2;
end;
procedure CenterX;
begin
X := R.Left + (R.Right-R.Left) div 2;
end;
procedure DrawEdit(clr: Tcolor);
begin
ACanvas.Pen.Color := clr;
CenterY;
CenterX;
ACanvas.MoveTo(X-2, Y-Dy);
ACanvas.LineTo(X+3, Y-Dy);
ACanvas.MoveTo(X, Y-Dy);
ACanvas.LineTo(X, Y+Dy);
ACanvas.MoveTo(X-2, Y+Dy);
ACanvas.LineTo(X+3, Y+Dy);
end;
begin
dx := 6;
dy := 6;
x := 0;
y := 0;
case Opt of
dsBrowse:
begin //
ACanvas.Brush.Color:=clBlack;
ACanvas.Pen.Color:=clBlack;
CenterY;
x:= R.Left+3;
if MultiSel then begin
if BiDiMode = bdRightToLeft then begin
ACanvas.Polyline([point(x+dx,y-dy), point(x,y),point(x+dx,y+dy), point(x+dx,y+dy-1)]);
ACanvas.Polyline([point(x+dx,y-dy+1), point(x+1,y),point(x+dx,y+dy-1), point(x+dx,y+dy-2)]);
CenterX;
Dec(X,3);
ACanvas.Ellipse(Rect(X+dx-2,Y-2,X+dx+2,Y+2));
end else begin
ACanvas.Polyline([point(x,y-dy), point(x+dx,y),point(x,y+dy), point(x,y+dy-1)]);
ACanvas.Polyline([point(x,y-dy+1),point(x+dx-1,y),point(x, y+dy-1), point(x,y+dy-2)]);
CenterX;
Dec(X,3);
ACanvas.Ellipse(Rect(X-2,Y-2,X+2,Y+2));
end;
end else begin
if BiDiMode = bdRightToLeft then
ACanvas.Polygon([point(x,y),point(x+dx,y-dy),point(x+dx, y+dy),point(x,y)])
else
ACanvas.Polygon([point(x,y-dy),point(x+dx,y),point(x, y+dy),point(x,y-dy)]);
end;
end;
dsEdit:
DrawEdit(clBlack);
dsInsert:
DrawEdit(clGreen);
else
if MultiSel then begin
ACanvas.Brush.Color:=clBlack;
ACanvas.Pen.Color:=clBlack;
CenterX;
CenterY;
ACanvas.Ellipse(Rect(X-3,Y-3,X+3,Y+3));
end;
end;
end;
function TCustomDBGrid.EditorCanAcceptKey(const ch: TUTF8Char): boolean;
var
aField: TField;
begin
result := False;
if FDataLink.Active then begin
aField := SelectedField;
if aField<>nil then begin
Result := IsValidChar(AField, Ch) and not aField.Calculated and
(aField.DataType<>ftAutoInc) and (aField.FieldKind<>fkLookup) and
(not aField.IsBlob or CheckDisplayMemo(aField));
end;
end;
end;
function TCustomDBGrid.EditorIsReadOnly: boolean;
var
AField : TField;
FieldList: TList;
I: Integer;
begin
Result := inherited EditorIsReadOnly;
if not Result then begin
AField := GetFieldFromGridColumn(Col);
if assigned(AField) then begin
// if field can't be modified, it's assumed readonly
result := not AField.CanModify;
// if field is readonly, check if it's a lookup field
if result and (AField.FieldKind = fkLookup) then begin
FieldList := TList.Create;
try
AField.DataSet.GetFieldList(FieldList, AField.KeyFields);
// check if any keyfields are there
result := (FieldList.Count=0); // if not simply is still readonly
// if yes assumed keyfields are modifiable
for I := 0 to FieldList.Count-1 do
if not TField(FieldList[I]).CanModify then begin
result := true; // at least one keyfield is readonly
break;
end;
finally
FieldList.Free;
end;
end;
// if it's not readonly and is not already editing, start editing.
if not result and not FDatalink.Editing then begin
Include(FGridStatus, gsStartEditing);
Result := not FDataLink.Edit;
Exclude(FGridStatus, gsStartEditing);
end;
end
else
result := true; // field is nil so it's readonly
end;
end;
procedure TCustomDBGrid.EditorTextChanged(const aCol, aRow: Integer;
const aText: string);
var
isReadOnly: Boolean;
begin
isReadOnly := EditorIsReadonly;
if not isReadOnly then
SetEditText(aCol, aRow, aText);
EditingColumn(Col, not isReadOnly);
end;
procedure TCustomDBGrid.HeaderSized(IsColumn: Boolean; Index: Integer);
var
i: Integer;
begin
if IsColumn then begin
if Columns.Enabled then begin
i := ColumnIndexFromGridColumn(Index);
if i>=0 then
Columns[i].Width := ColWidths[Index];
end;
FDefaultColWidths := False;
if Assigned(OnColumnSized) then
OnColumnSized(Self);
end;
end;
function TCustomDBGrid.IsColumnVisible(aCol: Integer): boolean;
var
gridcol: TGridColumn;
begin
if Columns.Enabled then begin
gridcol := ColumnFromGridColumn(aCol);
result := (gridcol<>nil) and gridCol.Visible;
end else
result := (aCol>=FirstGridColumn) and (ColWidths[aCol]>0);
end;
function TCustomDBGrid.IsValidChar(AField: TField; AChar: TUTF8Char): boolean;
begin
result := False;
if Length(AChar)>1 then begin
// problem: AField should validate a unicode char, but AField has no
// such facility, ask the user, if user is not interested
// do ansi convertion and try with field.
{ TODO: is this really necessary?
if Assigned(FOnValidateUTF8Char) then begin
result := true;
OnValidateUT8Char(Self, AField, AChar, Result)
exit;
end else
}
AChar := UTF8ToSys(AChar);
end else
if Length(AChar)=0 then
exit;
Result := (AChar[1]=#8) or AField.IsValidChar(AChar[1])
end;
procedure TCustomDBGrid.UpdateActive;
var
PrevRow: Integer;
NewRow: Integer;
begin
if (csDestroying in ComponentState) or
(FDatalink=nil) or (not FDatalink.Active) or
(FDatalink.ActiveRecord<0) then
exit;
{$ifdef dbgDBGrid}
DebugLn('%s.UpdateActive (%s): ActiveRecord=%d FixedRows=%d Row=%d',
[ClassName, Name, FDataLink.ActiveRecord, FixedRows, Row]);
{$endif}
PrevRow := Row;
NewRow:= FixedRows + FDataLink.ActiveRecord;
if NewRow>RowCount-1 then
NewRow := RowCount-1;
Row := NewRow;
if PrevRow<>Row then
InvalidateCell(0, PrevRow);//(InvalidateRow(PrevRow);
InvalidateRow(Row);
end;
function TCustomDBGrid.UpdateGridCounts: Integer;
var
RecCount: Integer;
FRCount, FCCount: Integer;
begin
// find out the column count, if result=0 then
// there are no visible columns defined or dataset is inactive
// or there are no visible fields, ie the grid is blank
{$ifdef dbgDBGrid}DebugLnEnter('%s.UpdateGridCounts INIT', [ClassName]);{$endif}
BeginUpdate;
try
Result := GetColumnCount;
if Result > 0 then begin
FRCount := FixedRowsExtra;
if dgTitles in Options then Inc(FRCount);
if dgIndicator in Options then FCCount := 1 else FCCount := 0;
InternalSetColCount(Result + FCCount);
if FDataLink.Active then begin
UpdateBufferCount;
RecCount := FDataLink.RecordCount;
if RecCount<1 then
RecCount := 1;
end else begin
RecCount := 0;
if FRCount=0 then
// need to be large enough to hold indicator
// if there is one, and if there are no titles
RecCount := FCCount;
end;
Inc(RecCount, FRCount);
RowCount := RecCount;
FixedRows := FRCount;
UpdateGridColumnSizes;
if FDatalink.Active and (FDatalink.ActiveRecord>=0) then
AdjustEditorBounds(Col, FixedRows + FDatalink.ActiveRecord);
end;
finally
EndUpdate;
end;
{$ifdef dbgDBGrid}DebugLnExit('%s.UpdateGridCounts DONE', [ClassName]);{$endif}
end;
constructor TCustomDBGrid.Create(AOwner: TComponent);
begin
FEditingColumn:=-1;
DragDx:=5;
inherited Create(AOwner);
FDataLink := TComponentDataLink.Create;//(Self);
FDataLink.OnRecordChanged:=@OnRecordChanged;
FDataLink.OnDatasetChanged:=@OnDataSetChanged;
FDataLink.OnDataSetOpen:=@OnDataSetOpen;
FDataLink.OnDataSetClose:=@OnDataSetClose;
FDataLink.OnNewDataSet:=@OnNewDataSet;
FDataLink.OnInvalidDataSet:=@OnInvalidDataset;
FDataLink.OnInvalidDataSource:=@OnInvalidDataSource;
FDataLink.OnDataSetScrolled:=@OnDataSetScrolled;
FDataLink.OnLayoutChanged:=@OnLayoutChanged;
FDataLink.OnEditingChanged:=@OnEditingChanged;
FDataLink.OnUpdateData:=@OnUpdateData;
FDatalink.OnFocusControl := @OnFocusControl;
FDataLink.VisualControl:= True;
FSelectedRows := TBookmarkList.Create(Self);
RenewColWidths;
FOptions := [dgColumnResize, dgColumnMove, dgTitles, dgIndicator, dgRowLines,
dgColLines, dgConfirmDelete, dgCancelOnExit, dgTabs, dgEditing,
dgAlwaysShowSelection];
inherited Options :=
[goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine,
goSmoothScroll, goColMoving, goTabs, goEditing, goDrawFocusSelected,
goColSizing ];
FExtraOptions := [dgeAutoColumns, dgeCheckboxColumn];
AutoAdvance := aaRightDown;
// What a dilema!, we need ssAutoHorizontal and ssVertical!!!
ScrollBars:=ssBoth;
AllowOutboundEvents := false;
end;
procedure TCustomDBGrid.AutoAdjustColumns;
begin
Exclude(FGridStatus, gsAutoSized);
UpdateAutoSizeColumns;
end;
procedure TCustomDBGrid.InitiateAction;
begin
{$ifdef dbgDBGrid}DebugLnEnter('%s.InitiateAction INIT', [ClassName]);{$endif}
inherited InitiateAction;
if (gsUpdatingData in FGridStatus) then begin
EndUpdating;
{
if EditorMode then begin
Editor.SetFocus;
EditorSelectAll;
end;
}
end;
{$ifdef dbgDBGrid}DebugLnExit('%s.InitiateAction DONE', [ClassName]);{$endif}
end;
procedure TCustomDBGrid.DefaultDrawColumnCell(const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
S: string;
F: TField;
DataRow: Integer;
begin
F := Column.Field;
DataCol := GridColumnFromColumnIndex(DataCol);
if FDataLink.Active then
DataRow := FixedRows + FDataLink.ActiveRecord
else
DataRow := 0;
if DataCol>=FirstGridColumn then
case ColumnEditorStyle(DataCol, F) of
cbsCheckBoxColumn:
DrawCheckBoxBitmaps(DataCol, Rect, F);
else begin
if F<>nil then begin
if CheckDisplayMemo(F) then
S := F.AsString
else
if F.dataType <> ftBlob then
S := F.DisplayText
else
S := '(blob)';
end else
S := '';
DrawCellText(DataCol, DataRow, Rect, State, S);
end;
end;
end;
function TCustomDBGrid.EditorByStyle(Style: TColumnButtonStyle): TWinControl;
begin
// we want override the editor style if it is cbsAuto because
// field.datatype might be ftBoolean or some other cases
if Style=cbsAuto then
Style := ColumnEditorStyle(Col, SelectedField);
Result:=inherited EditorByStyle(Style);
end;
procedure TCustomDBGrid.ResetColWidths;
begin
if not FDefaultColWidths then begin
RenewColWidths;
LayoutChanged;
end;
end;
procedure TCustomDBGrid.SelectRecord(AValue: boolean);
begin
{$ifdef dbgGrid}DebugLn('%s.SelectRecord', [ClassName]); {$endif}
if dgMultiSelect in Options then
FSelectedRows.CurrentRowSelected := AValue;
end;
procedure TCustomDBGrid.GetScrollbarParams(out aRange, aPage, aPos: Integer);
begin
if (FDatalink<>nil) and FDatalink.Active then begin
if FDatalink.dataset.IsSequenced then begin
aRange := GetRecordCount + VisibleRowCount - 1;
aPage := VisibleRowCount;
if aPage<1 then aPage := 1;
if FDatalink.BOF then aPos := 0 else
if FDatalink.EOF then aPos := aRange
else
begin
aPos := FDataLink.DataSet.RecNo - 1; // RecNo is 1 based
FDataLink.DataSet.UpdateCursorPos; // FPC 3 bug #31532 workaround
end;
if aPos<0 then aPos:=0;
if aRange=0 then aRange:=1; // there's always 1 (new) row
end else begin
aRange := 6;
aPage := 2;
if FDatalink.EOF then aPos := 4 else
if FDatalink.BOF then aPos := 0
else aPos := 2;
end;
end else begin
aRange := 0;
aPage := 0;
aPos := 0;
end;
end;
procedure TCustomDBGrid.CMGetDataLink(var Message: TLMessage);
begin
Message.Result := PtrUInt(FDataLink);
end;
procedure TCustomDBGrid.ClearSelection(selCurrent:boolean=false);
begin
if [dgMultiSelect,dgPersistentMultiSelect]*Options=[dgMultiSelect] then begin
if SelectedRows.Count>0 then
SelectedRows.Clear;
if SelCurrent then
SelectRecord(true);
end;
end;
function TCustomDBGrid.NeedAutoSizeColumns: boolean;
begin
result := (dgAutoSizeColumns in Options)
//and (HandleAllocated)
;
end;
procedure TCustomDBGrid.RenewColWidths;
begin
FDefaultColWidths := True;
exclude(FGridStatus, gsAutoSized);
end;
procedure TCustomDBGrid.InternalAutoSizeColumn(aCol: Integer; aCanvas: TCanvas; aDatalinkActive: Boolean);
var
Field: TField;
C: TGridColumn;
ColWidth: Integer;
ARow,w: Integer;
s: string;
begin
Field := GetFieldFromGridColumn(ACol);
C := ColumnFromGridColumn(ACol);
if (C<>nil) and (C.Title<>nil) then begin
aCanvas.Font := C.Title.Font;
ColWidth := aCanvas.TextWidth(trim(C.Title.Caption));
aCanvas.Font := C.Font;
end else begin
if (Field<>nil) then begin
aCanvas.Font := TitleFont;
ColWidth := aCanvas.TextWidth(Field.FieldName);
end
else
ColWidth := 0;
aCanvas.Font := Font;
end;
if (Field<>nil) and aDatalinkActive then
for ARow := FixedRows to RowCount-1 do begin
FDatalink.ActiveRecord := ARow - FixedRows;
if CheckDisplayMemo(Field) then
s := Field.AsString
else if Field.dataType<>ftBlob then
s := trim(Field.DisplayText)
else
s := '(blob)';
w := aCanvas.TextWidth(s);
if w>ColWidth then
ColWidth := w;
end;
if ColWidth=0 then
ColWidth := GetColumnWidth(ACol);
ColWidths[ACol] := ColWidth + 15;
end;
destructor TCustomDBGrid.Destroy;
begin
{$ifdef dbgGrid}DebugLn('%s.Destroy', [ClassName]); {$endif}
FSelectedRows.Free;
FDataLink.OnDataSetChanged:=nil;
FDataLink.OnRecordChanged:=nil;
FDataLink.Free;
inherited Destroy;
end;
function TCustomDBGrid.MouseToRecordOffset(const x, y: Integer; out
Column: TColumn; out RecordOffset: Integer): TGridZone;
var
aCol,aRow: Integer;
begin
Result := MouseToGridZone(x, y);
Column := nil;
RecordOffset := 0;
if (Result=gzInvalid) or (Result=gzFixedCells) then
exit;
MouseToCell(x, y, aCol, aRow);
if (Result=gzFixedRows) or (Result=gzNormal) then
RecordOffset := aRow - Row;
if (Result=gzFixedCols) or (Result=gzNormal) then begin
aRow := ColumnIndexFromGridColumn(aCol);
if aRow>=0 then
Column := Columns[aRow];
end;
end;
function TCustomDBGrid.ExecuteAction(AAction: TBasicAction): Boolean;
begin
Result := (DataLink <> nil)
and DataLink.ExecuteAction(AAction);
end;
function TCustomDBGrid.UpdateAction(AAction: TBasicAction): Boolean;
begin
Result := (DataLink <> nil)
and DataLink.UpdateAction(AAction);
end;
procedure TCustomDBGrid.SaveToFile(FileName: string);
begin
SaveOptions:=[ soDesign ];
inherited SaveToFile(Filename);
end;
procedure TCustomDBGrid.SaveToStream(AStream: TStream);
begin
SaveOptions:=[ soDesign ];
inherited SaveToStream(AStream);
end;
procedure TCustomDBGrid.LoadFromFile(FileName: string);
begin
SaveOptions:=[ soDesign ];
Include(FGridStatus, gsLoadingGrid);
inherited LoadFromFile(Filename);
Exclude(FGridStatus, gsLoadingGrid);
end;
procedure TCustomDBGrid.LoadFromStream(AStream: TStream);
begin
SaveOptions:=[ soDesign ];
Include(FGridStatus, gsLoadingGrid);
inherited LoadFromStream(AStream);
Exclude(FGridStatus, gsLoadingGrid);
end;
{ TComponentDataLink }
function TComponentDataLink.GetFields(Index: Integer): TField;
begin
{$ifdef dbgGrid}DebugLn('%s.GetFields Index=%d',[ClassName, Index]); {$endif}
if (index>=0) and (index<DataSet.FieldCount) then
result:=DataSet.Fields[index]
else
result:=nil;
end;
function TComponentDataLink.GetDataSetName: string;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.GetDataSetName', [ClassName]);
{$endif}
Result:=FDataSetName;
if DataSet<>nil then Result:=DataSet.Name;
end;
procedure TComponentDataLink.SetDataSetName(const AValue: string);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.SetDataSetName', [ClassName]);
{$endif}
if FDataSetName<>AValue then FDataSetName:=AValue;
end;
procedure TComponentDataLink.RecordChanged(Field: TField);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.RecordChanged', [ClassName]);
{$endif}
if Assigned(OnRecordChanged) then
OnRecordChanged(Field);
end;
procedure TComponentDataLink.DataSetChanged;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.DataSetChanged FirstRecord=%d', [ClassName, FirstRecord]);
{$endif}
if Assigned(OnDataSetChanged) then
OnDataSetChanged(DataSet);
end;
procedure TComponentDataLink.ActiveChanged;
begin
{$ifdef dbgDBGrid}
DebugLnEnter('%s.ActiveChanged INIT', [ClassName]);
{$endif}
if Active then begin
fDataSet := DataSet;
if DataSetName <> fDataSetName then begin
fDataSetName := DataSetName;
if Assigned(fOnNewDataSet) then fOnNewDataSet(DataSet);
end else
if Assigned(fOnDataSetOpen) then fOnDataSetOpen(DataSet);
end else begin
BufferCount := 0;
if (DataSource = nil)then begin
if Assigned(fOnInvalidDataSource) then fOnInvalidDataSource(fDataSet);
fDataSet := nil;
fDataSetName := '[???]';
end else begin
if (DataSet=nil)or(csDestroying in DataSet.ComponentState) then begin
if Assigned(fOnInvalidDataSet) then fOnInvalidDataSet(fDataSet);
fDataSet := nil;
fDataSetName := '[???]';
end else begin
if Assigned(FOnDataSetClose) then begin
FOnDataSetClose(DataSet);
{$ifdef dbgDBGrid} DebugLn('%s.ActiveChanged OnDataSetClose Called', [ClassName]); {$endif}
end;
if DataSet <> nil then FDataSetName := DataSetName;
end;
end;
end;
{$ifdef dbgDBGrid}
DebugLnExit('%s.ActiveChanged DONE', [ClassName]);
{$endif}
end;
procedure TComponentDataLink.LayoutChanged;
begin
{$ifdef dbgDBGrid}
DebugLnEnter('%s.LayoutChanged INIT', [ClassName]);
{$endif}
if Assigned(OnLayoutChanged) then
OnLayoutChanged(DataSet);
{$ifdef dbgDBGrid}
DebugLnExit('%s.LayoutChanged DONE', [ClassName]);
{$endif}
end;
procedure TComponentDataLink.DataSetScrolled(Distance: Integer);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.DataSetScrolled Distance=%d',[ClassName, Distance]);
{$endif}
if Assigned(OnDataSetScrolled) then
OnDataSetScrolled(DataSet, Distance);
end;
procedure TComponentDataLink.FocusControl(Field: TFieldRef);
begin
{$ifdef dbgDBGrid}
DebugLn('%s.FocusControl', [ClassName]);
{$endif}
if Assigned(OnFocusControl) then
OnFocusControl(Field);
end;
procedure TComponentDataLink.CheckBrowseMode;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.CheckBrowseMode', [ClassName]);
{$endif}
inherited CheckBrowseMode;
end;
procedure TComponentDataLink.EditingChanged;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.EditingChanged', [ClassName]);
{$endif}
if Assigned(OnEditingChanged) then
OnEditingChanged(DataSet);
end;
procedure TComponentDataLink.UpdateData;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.UpdateData', [ClassName]);
{$endif}
if Assigned(OnUpdatedata) then
OnUpdateData(DataSet);
end;
function TComponentDataLink.MoveBy(Distance: Integer): Integer;
begin
(*
{$ifdef dbgDBGrid}
DebugLnEnter('%s.MoveBy INIT Distance=%d',[ClassName, Distance]);
{$endif}
*)
Result:=inherited MoveBy(Distance);
(*
{$ifdef dbgDBGrid}
DebugLnExit('%s.MoveBy DONE Result=%d',[ClassName, Result]);
{$endif}
*)
end;
{ TDBGridColumns }
function TDBGridColumns.GetColumn(Index: Integer): TColumn;
begin
result := TColumn( inherited Items[Index] );
end;
procedure TDBGridColumns.SetColumn(Index: Integer; Value: TColumn);
begin
Items[Index].Assign( Value );
end;
procedure TDBGridColumns.Update(Item: TCollectionItem);
begin
if (Grid<>nil) and not (csLoading in Grid.ComponentState) then
TCustomDBGrid(Grid).LayoutChanged;
end;
function TDBGridColumns.ColumnFromField(Field: TField): TColumn;
var
i: Integer;
begin
if Field<>nil then
for i:=0 to Count-1 do begin
result := Items[i];
if (result<>nil)and(result.Field=Field) then
exit;
end;
result:=nil;
end;
function TDBGridColumns.HasAutomaticColumns: boolean;
var
i: Integer;
begin
Result := False;
for i:=0 to Count-1 do
if Items[i].IsAutomaticColumn then begin
Result := true;
break;
end;
end;
function TDBGridColumns.HasDesignColumns: boolean;
var
i: Integer;
begin
Result := False;
for i:=0 to Count-1 do
if Items[i].IsDesignColumn then begin
Result := true;
break;
end;
end;
procedure TDBGridColumns.RemoveAutoColumns;
var
i: Integer;
G: TCustomDBGrid;
begin
if HasAutomaticColumns then begin
G := TCustomDBGrid(Grid);
G.GridStatus := G.GridStatus + [gsRemovingAutoColumns];
BeginUpdate;
try
for i:=Count-1 downto 0 do
if Items[i].IsAutomaticColumn then
Delete(i);
finally
EndUpdate;
G.GridStatus := G.GridStatus - [gsRemovingAutoColumns];
end;
end;
end;
function CompareFieldIndex(P1,P2:Pointer): integer;
begin
if P1=P2 then
Result := 0
else if (P1=nil) or (TColumn(P1).Field=nil) then
Result := 1
else if (P2=nil) or (TColumn(P2).Field=nil) then
Result := -1
else
Result := TColumn(P1).Field.Index - TColumn(P2).Field.Index;
end;
function CompareDesignIndex(P1,P2:Pointer): integer;
begin
result := TColumn(P1).DesignIndex - TColumn(P2).DesignIndex;
end;
procedure TDBGridColumns.ResetColumnsOrder(ColumnOrder: TColumnOrder);
var
L: TList;
i: Integer;
begin
L := TList.Create;
try
for i:=0 to Count-1 do
L.Add(Items[i]);
case ColumnOrder of
coDesignOrder:
begin
if not HasDesignColumns then
exit;
L.Sort(@CompareDesignIndex)
end;
coFieldIndexOrder:
L.Sort(@CompareFieldIndex);
else
exit;
end;
for i:=0 to L.Count-1 do
TColumn(L.Items[i]).Index := i;
finally
L.Free;
end;
end;
function TDBGridColumns.Add: TColumn;
var
G: TCustomDBGrid;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.Add', [ClassName]);
{$endif}
G := TCustomDBGrid(Grid);
if G<>nil then begin
// remove automatic columns before adding user columns
if not (gsAddingAutoColumns in G.GridStatus) then
RemoveAutoColumns;
end;
result := TColumn( inherited add );
end;
function TDBGridColumns.ColumnByFieldname(const aFieldname: string): TColumn;
var
i: Integer;
begin
result := nil;
for i:=0 to Count-1 do
if CompareText(Items[i].FieldName, aFieldname)=0 then begin
result := Items[i];
break;
end;
end;
function TDBGridColumns.ColumnByTitle(const aTitle: string): TColumn;
begin
result := TColumn(inherited ColumnByTitle(aTitle));
end;
procedure TDBGridColumns.LinkFields;
var
i: Integer;
G: TCustomDBGrid;
begin
G := TCustomDBGrid(Grid);
if G<>nil then
G.BeginLayout;
for i:=0 to Count-1 do
Items[i].LinkField;
if G<>nil then
G.EndLayout;
end;
{ TColumn }
function TColumn.GetField: TField;
begin
if (FFieldName<>'') and (FField<>nil) then
LinkField;
result := FField;
end;
function TColumn.GetIsDesignColumn: boolean;
begin
result := (DesignIndex>=0) and (DesignIndex<10000);
end;
function TColumn.GetPickList: TStrings;
begin
Result := inherited GetPickList;
if (Field<>nil) and (FField.FieldKind=fkLookup) then
begin
if FField.LookupCache then
FField.LookupList.ValuesToStrings(Result)
else
begin
Result.Clear;
LookupGetBookMark(FField);
try
with FField.LookupDataSet do
begin
First;
while not EOF do
begin
Result.Add(FieldbyName(FField.LookupResultField).AsString);
Next;
end;
end;
finally
LookupGotoBookMark(FField);
end;
end;
end;
end;
procedure TColumn.ApplyDisplayFormat;
begin
if (FField <> nil) and FDisplayFormatChanged then begin
if (FField is TNumericField) then
TNumericField(FField).DisplayFormat := DisplayFormat
else if (FField is TDateTimeField) then
TDateTimeField(FField).DisplayFormat := DisplayFormat;
end;
end;
function TColumn.GetDisplayFormat: string;
begin
if not FDisplayFormatChanged then
Result := GetDefaultDisplayFormat
else
result := FDisplayFormat;
end;
function TColumn.IsDisplayFormatStored: boolean;
begin
Result := FDisplayFormatChanged;
end;
procedure TColumn.SetDisplayFormat(const AValue: string);
begin
if (not FDisplayFormatChanged)or(CompareText(AValue, FDisplayFormat)<>0) then begin
FDisplayFormat := AValue;
FDisplayFormatChanged:=True;
ColumnChanged;
end;
end;
procedure TColumn.SetField(const AValue: TField);
begin
if FField <> AValue then begin
FField := AValue;
if FField<>nil then
FFieldName := FField.FieldName;
ColumnChanged;
end;
end;
procedure TColumn.SetFieldName(const AValue: String);
begin
if FFieldName=AValue then exit;
FFieldName:=AValue;
LinkField;
ColumnChanged;
end;
function TColumn.GetDataSet: TDataSet;
var
AGrid: TCustomDBGrid;
begin
AGrid := TCustomDBGrid(Grid);
if (AGrid<>nil) then
result := AGrid.FDataLink.DataSet
else
result :=nil;
end;
procedure TColumn.Assign(Source: TPersistent);
begin
if Source is TColumn then begin
//DebugLn('Assigning TColumn[',dbgs(Index),'] a TColumn')
Collection.BeginUpdate;
try
inherited Assign(Source);
FieldName := TColumn(Source).FieldName;
DisplayFormat := TColumn(Source).DisplayFormat;
ValueChecked := TColumn(Source).ValueChecked;
ValueUnchecked := TColumn(Source).ValueUnchecked;
finally
Collection.EndUpdate;
end;
end else
inherited Assign(Source);
end;
function TColumn.GetDefaultWidth: Integer;
var
AGrid: TCustomDBGrid;
tmpCanvas: TCanvas;
begin
AGrid := TCustomDBGrid(Grid);
if AGrid<>nil then begin
tmpCanvas := GetWorkingCanvas(aGrid.Canvas);
tmpCanvas.Font := aGrid.Font;
if FField<>nil then
result := CalcColumnFieldWidth(
tmpCanvas,
dgTitles in aGrid.Options,
Title.Caption,
Title.Font,
FField)
else
result := AGrid.DefaultColWidth;
if tmpCanvas<>AGrid.Canvas then
FreeWorkingCanvas(tmpCanvas);
end else
result := -1;
end;
function TColumn.CreateTitle: TGridColumnTitle;
begin
Result := TColumnTitle.Create(Self);
end;
constructor TColumn.Create(ACollection: TCollection);
var
AGrid: TCustomGrid;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.Create', [ClassName]);
{$endif}
inherited Create(ACollection);
if ACollection is TDBGridColumns then begin
AGrid := TDBGridColumns(ACollection).Grid;
if (AGrid<>nil) and (csLoading in AGrid.ComponentState) then
FDesignIndex := Index
else
FDesignIndex := 10000;
end;
end;
function TColumn.IsDefault: boolean;
begin
result := not FDisplayFormatChanged and (inherited IsDefault());
end;
procedure TColumn.LinkField;
var
AGrid: TCustomDBGrid;
begin
AGrid:= TCustomDBGrid(Grid);
if (AGrid<>nil) and AGrid.FDatalink.Active then begin
Field := AGrid.FDataLink.DataSet.FindField(FFieldName);
ApplyDisplayFormat;
end else
Field := nil;
end;
function TColumn.GetDefaultDisplayFormat: string;
begin
Result := '';
if FField<>nil then begin
if FField is TNumericField then
Result := TNumericField(FField).DisplayFormat
else if FField is TDateTimeField then
Result := TDateTimeField(FField).DisplayFormat
end;
end;
function TColumn.GetDefaultValueChecked: string;
begin
if (FField<>nil) and (FField.Datatype=ftBoolean) then
Result := BoolToStr(True)
else
Result := '1';
end;
function TColumn.GetDefaultValueUnchecked: string;
begin
if (FField<>nil) and (FField.DataType=ftBoolean) then
Result := BoolToStr(False)
else
Result := '0';
end;
function TColumn.GetDefaultReadOnly: boolean;
var
AGrid: TCustomDBGrid;
begin
AGrid := TCustomDBGrid(Grid);
Result := ((AGrid<>nil)and(AGrid.ReadOnly)) or ((FField<>nil)And(FField.ReadOnly))
end;
function TColumn.GetDefaultVisible: boolean;
begin
if FField<>nil then
result := FField.Visible
else
result := True;
end;
function TColumn.GetDisplayName: string;
begin
if FFieldName<>'' then
Result:=FFieldName
else
Result:=inherited GetDisplayName;
end;
function TColumn.GetDefaultAlignment: TAlignment;
var
Bs: set of TColumnButtonStyle;
begin
bs := [buttonStyle];
if Grid<>nil then
Include(bs, TCustomDbGrid(Grid).DefaultEditorStyle(ButtonStyle, FField));
if bs*[cbsCheckboxColumn,cbsButtonColumn]<>[] then
result := taCenter
else
if FField<>nil then
result := FField.Alignment
else
Result := taLeftJustify;
end;
{ TColumnTitle }
function TColumnTitle.GetDefaultCaption: string;
begin
with (Column as TColumn) do begin
if FieldName<>'' then begin
if FField<>nil then
Result := FField.DisplayName
else
Result := Fieldname;
end else
Result := inherited GetDefaultCaption;
end;
end;
{ TBookmarkList }
function TBookmarkList.GetCount: integer;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.GetCount FList.Count=%d',[ClassName, FList.Count]);
{$endif}
result := FList.Count;
end;
function TBookmarkList.GetCurrentRowSelected: boolean;
var
Bookmark: TBookmark;
begin
CheckActive;
Bookmark := FDataset.GetBookmark;
Result := IndexOf(Bookmark)>=0;
FDataset.FreeBookmark(Bookmark);
end;
function TBookmarkList.GetItem(AIndex: Integer): TBookmark;
begin
Result := TBookmark(FList[AIndex]);
end;
procedure TBookmarkList.SetCurrentRowSelected(const AValue: boolean);
var
Bookmark: pointer;
Index: Integer;
begin
CheckActive;
Bookmark := nil;
TBookmark(Bookmark) := FDataset.GetBookmark; // fetch and increase reference count
if Bookmark = nil then
Exit;
if Find(Bookmark, Index) then begin
FDataset.FreeBookmark(Bookmark);
{$ifndef noautomatedbookmark}
SetLength(TBookmark(Bookmark),0); // decrease reference count
{$endif noautomatedbookmark}
if not AValue then begin
FDataset.FreeBookmark(Pointer(Items[Index]));
{$ifndef noautomatedbookmark}
Bookmark := FList[Index];
SetLength(TBookmark(Bookmark),0); // decrease reference count
{$endif noautomatedbookmark}
FList.Delete(Index);
FGrid.Invalidate;
end;
end else begin
if AValue then begin
// the reference count of Bookmark was increased above, so it is save to
// store it here as pointer
FList.Insert(Index, Bookmark);
FGrid.Invalidate;
end else
begin
FDataset.FreeBookmark(Bookmark);
{$ifndef noautomatedbookmark}
SetLength(TBookmark(Bookmark),0); // decrease reference count
{$endif}
end;
end;
end;
procedure TBookmarkList.CheckActive;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.CheckActive', [ClassName]);
{$endif}
if not FGrid.FDataLink.Active then
raise EInvalidGridOperation.Create('Dataset Inactive');
if FGrid.DataSource.DataSet=FDataset then
exit;
FDataset := FGrid.DataSource.DataSet;
// Note.
//
// fpc help say CompareBookmarks should return -1, 0 or 1 ... which imply that
// bookmarks should be a sorted array (or list). In this scenario binary search
// is the prefered method for finding a bookmark.
//
// The problem here is that TBufDataset and TSQLQuery (and thus TCustomSQLQuery
// and TCustomBufDataset) CompareBookmarks only return 0 or -1 (some kind of
// is this a valid bookmark or not), the result is that it appears as an unsorted
// list (or array) and binary search should not be used.
//
// The weird thing is that if we use MyCompareBookmarks which deals with comparing
// the memory reserved for bookmarks in the hope bookmarks are just some kind of
// reocord indexes, currently work fine for TCustomBufDataset derived datasets.
// however using CompareBookmarks is always the right thing to use where implemented.
//
// As Dbgrid should be TDataset implementation agnostic this is a way I found
// to know if the dataset is derived from TCustomBufDataset or not.
// Once TCustomBufDataset is fixed, remove this ugly note & hack.
case FDataset.ClassName of
'TSQLQuery','TBufDataset','TCustomSQLQuery','TCustomBufDataset':
FCanDoBinarySearch := false;
else
FCanDoBinarySearch := true;
end;
end;
function TBookmarkList.GetEnumerator(opt: TBookmarkedRecordEnumeratorOptions
): TBookmarkedRecordEnumerator;
begin
result := TBookmarkedRecordEnumerator.Create(self, fGrid, opt);
end;
constructor TBookmarkList.Create(AGrid: TCustomDbGrid);
begin
inherited Create;
FGrid := AGrid;
FList := TFPList.Create;
end;
destructor TBookmarkList.Destroy;
begin
Clear;
FreeAndNil(FList);
inherited Destroy;
end;
procedure TBookmarkList.Clear;
var
i: Integer;
{$ifndef noautomatedbookmark}
Bookmark: Pointer;
{$endif}
begin
for i:=0 to FList.Count-1 do
begin
{$ifdef dbgDBGrid}
DebugLn('%s.Clear', [ClassName]);
{$endif}
FDataset.FreeBookmark(Items[i]);
{$ifndef noautomatedbookmark}
Bookmark := FList[i];
SetLength(TBookmark(Bookmark),0); // decrease reference count
{$endif noautomatedbookmark}
end;
FList.Clear;
FGrid.Invalidate;
end;
procedure TBookmarkList.Delete;
var
i: Integer;
{$ifndef noautomatedbookmark}
Bookmark: Pointer;
{$endif}
begin
{$ifdef dbgDBGrid}
DebugLn('%s.Delete', [ClassName]);
{$endif}
for i := FList.Count-1 downto 0 do begin
FDataset.GotoBookmark(Items[i]);
{$ifndef noautomatedbookmark}
Bookmark := FList[i];
SetLength(TBookmark(Bookmark),0); // decrease reference count
{$else}
FDataset.FreeBookmark(Items[i]);
{$endif noautomatedbookmark}
FDataset.Delete;
FList.Delete(i);
end;
end;
type
TDs=class(TDataset)
end;
function TBookmarkList.Find(const Item: TBookmark; var AIndex: Integer): boolean;
var
L, R, I: Integer;
CompareRes: Integer;
procedure BinarySearch;
begin
L := 0;
R := FList.Count - 1;
while (L <= R) do
begin
I := L + (R - L) div 2;
CompareRes := FDataset.CompareBookmarks(Item, TBookmark(FList[I]));
if (CompareRes > 0) then
L := I + 1
else
begin
R := I - 1;
if (CompareRes = 0) then
begin
Result := True;
L := I;
end;
end;
end;
AIndex := L;
end;
procedure VisitAll;
begin
AIndex := 0;
i := 0;
while i<FList.Count do begin
CompareRes := FDataset.CompareBookmarks(Item, TBookmark(FList[I]));
if CompareRes=0 then begin
result := true;
AIndex := i;
exit;
end;
inc(i);
end;
end;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.Find', [ClassName]);
{$endif}
Result := False;
if Item=nil then
Exit;
if FCanDoBinarySearch then
BinarySearch
else
VisitAll;
end;
function TBookmarkList.IndexOf(const Item: TBookmark): Integer;
begin
{$ifdef dbgDBGrid}
DebugLn('%s.IndexOf', [ClassName]);
{$endif}
if not Find(Item, Result) then
Result := -1;
end;
function TBookmarkList.Refresh: boolean;
var
i: LongInt;
{$ifndef noautomatedbookmark}
Bookmark: Pointer;
{$endif}
begin
{$ifdef dbgDBGrid}
DebugLn('%s.Refresh', [ClassName]);
{$endif}
Result := False;
for i := FList.Count - 1 downto 0 do
if not FDataset.BookmarkValid(TBookMark(Items[i])) then begin
Result := True;
FDataset.FreeBookmark(Items[i]);
{$ifndef noautomatedbookmark}
Bookmark := FList[i];
SetLength(TBookmark(Bookmark),0); // decrease reference count
{$endif noautomatedbookmark}
Flist.Delete(i);
end;
if Result then
FGrid.Invalidate;
end;
end.