mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 19:38:04 +02:00
4868 lines
135 KiB
ObjectPascal
4868 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;
|
|
FUseCompareBookmarks: boolean;
|
|
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;
|
|
FKeyBookmark: TBookmark;
|
|
FKeySign: Integer;
|
|
FSavedRecord: Integer;
|
|
FOnGetCellHint: TDbGridCellHintEvent;
|
|
FOnRowMoved: TMovedEvent;
|
|
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 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 ResetSizes; override;
|
|
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 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 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.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;
|
|
if FKeyBookmark<>nil then begin
|
|
FDatalink.DataSet.FreeBookmark(FKeyBookmark);
|
|
FKeyBookmark:=nil;
|
|
end;
|
|
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, 1);
|
|
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;
|
|
if FKeyBookmark<>nil then begin
|
|
FDatalink.DataSet.FreeBookmark(FKeyBookmark);
|
|
FKeyBookmark:=nil;
|
|
end;
|
|
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;
|
|
|
|
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: Integer;
|
|
CurBookmark: TBookmark;
|
|
begin
|
|
if dgPersistentMultiSelect in Options then
|
|
exit;
|
|
|
|
if (ssShift in Shift) then begin
|
|
|
|
CurBookmark := FDatalink.DataSet.GetBookmark;
|
|
if FKeyBookmark=nil then
|
|
FKeyBookmark:=CurBookmark;
|
|
|
|
if (FKeyBookmark=CurBookmark) then begin
|
|
if AStart then begin
|
|
SelectRecord(true);
|
|
if ADown then
|
|
FKeySign := 1
|
|
else
|
|
FKeySign := -1;
|
|
exit;
|
|
end;
|
|
FKeySign := 0;
|
|
end else
|
|
FDatalink.DataSet.FreeBookmark(CurBookmark);
|
|
|
|
n := 4*Ord(FKeySign>=0) + 2*Ord(ADown) + 1*Ord(AStart);
|
|
case n of
|
|
0,6,8..11:
|
|
begin
|
|
SelectRecord(True);
|
|
end;
|
|
3,5:
|
|
begin
|
|
SelectRecord(False);
|
|
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
|
|
|
|
if FKeyBookmark<>nil then begin
|
|
FDatalink.DataSet.FreeBookmark(FKeyBookmark);
|
|
FKeyBookmark:=nil; // force new keyboard selection start
|
|
end;
|
|
|
|
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 then
|
|
ClearSelection(true)
|
|
// Select row before popupmenu
|
|
else 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.ResetSizes;
|
|
begin
|
|
LayoutChanged;
|
|
inherited ResetSizes;
|
|
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);
|
|
begin
|
|
// Draw focused cell if we have the focus
|
|
if Self.Focused and (dgAlwaysShowSelection in Options) and
|
|
FDatalink.Active and DefaultDrawing then
|
|
begin
|
|
CalcFocusRect(aRect);
|
|
DrawRubberRect(Canvas, aRect, FocusColor);
|
|
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 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
|
|
if dgTitles in Options then FRCount := 1 else FRCount := 0;
|
|
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;
|
|
if FKeyBookmark<>nil then begin
|
|
FDatalink.DataSet.FreeBookmark(FKeyBookmark);
|
|
FKeyBookmark:=nil;
|
|
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 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
|
|
FDataset.FreeBookmark(Bookmark);
|
|
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.
|
|
//
|
|
// Some dataset descendants do not implement CompareBookmarks, for these we
|
|
// use MyCompareBookmarks in the hope the allocated bookmark memory is used
|
|
// to hold some kind of record index.
|
|
FUseCompareBookmarks := TMethod(@FDataset.CompareBookmarks).Code<>pointer(@TDataset.CompareBookmarks);
|
|
|
|
// 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 MyCompareBookmarks(ds:Tdataset; b1,b2:pointer): Integer;
|
|
begin
|
|
if b1=b2 then
|
|
result := 0
|
|
else
|
|
if b1=nil then
|
|
result := 1
|
|
else
|
|
if b2=nil then
|
|
result := -1
|
|
else begin
|
|
// Note: Tds(ds).bookmarksize is set at creation of TDataSet and does not change
|
|
result := CompareMemRange(b1,b2,Tds(ds).bookmarksize);
|
|
end;
|
|
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;
|
|
if FUseCompareBookmarks then
|
|
CompareRes := FDataset.CompareBookmarks(Item, TBookmark(FList[I]))
|
|
else
|
|
CompareRes := MyCompareBookmarks(FDataset, pointer(Item), 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
|
|
if FUseCompareBookmarks then
|
|
CompareRes := FDataset.CompareBookmarks(Item, TBookmark(FList[I]))
|
|
else
|
|
CompareRes := MyCompareBookmarks(FDataset, pointer(Item), 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.
|