mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-16 18:49:59 +02:00

This reverts commit 72344a65f7
.
These changes break compatibility (change signatures of virtual
methods), potentially unsafe (these strings can be potentially changed
through user callbacks and this will lead to crashes) and provide very
little (if at all) performance improvements.
13952 lines
399 KiB
ObjectPascal
13952 lines
399 KiB
ObjectPascal
{ $Id$}
|
|
{
|
|
/***************************************************************************
|
|
Grids.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.
|
|
*****************************************************************************
|
|
}
|
|
{
|
|
|
|
TCustomGrid, TDrawGrid and TStringGrid for Lazarus
|
|
Copyright (C) 2002 Jesus Reyes Aguilar.
|
|
email: jesusrmx@yahoo.com.mx
|
|
|
|
}
|
|
|
|
unit Grids;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$modeswitch nestedprocvars}
|
|
{$define NewCols}
|
|
|
|
interface
|
|
|
|
uses
|
|
// RTL + FCL
|
|
Classes, SysUtils, Types, TypInfo, Math, FPCanvas, HtmlDefs, StrUtils,
|
|
// LCL
|
|
LCLStrConsts, LCLType, LCLIntf, Controls, Graphics, Forms,
|
|
LMessages, StdCtrls, LResources, MaskEdit, Buttons, Clipbrd, Themes, imglist,
|
|
// LazUtils
|
|
LazFileUtils, DynamicArray, Maps, LazUTF8, Laz2_XMLCfg,
|
|
LazLoggerBase, LazUtilities, LCSVUtils, IntegerList
|
|
{$ifdef WINDOWS}
|
|
,messages, imm
|
|
{$endif}
|
|
,extctrls;
|
|
|
|
const
|
|
//GRIDFILEVERSION = 1; // Original
|
|
//GRIDFILEVERSION = 2; // Introduced goSmoothScroll
|
|
GRIDFILEVERSION = 3; // Introduced Col/Row FixedAttr and NormalAttr
|
|
|
|
const
|
|
GM_SETVALUE = LM_INTERFACELAST + 100;
|
|
GM_GETVALUE = LM_INTERFACELAST + 101;
|
|
GM_SETGRID = LM_INTERFACELAST + 102;
|
|
GM_SETBOUNDS = LM_INTERFACELAST + 103;
|
|
GM_SELECTALL = LM_INTERFACELAST + 104;
|
|
GM_SETMASK = LM_INTERFACELAST + 105;
|
|
GM_SETPOS = LM_INTERFACELAST + 106;
|
|
GM_READY = LM_INTERFACELAST + 107;
|
|
GM_GETGRID = LM_INTERFACELAST + 108;
|
|
|
|
|
|
const
|
|
EO_AUTOSIZE = $1;
|
|
EO_HOOKKEYDOWN = $2;
|
|
EO_HOOKKEYPRESS = $4;
|
|
EO_HOOKKEYUP = $8;
|
|
EO_SELECTALL = $10;
|
|
EO_IMPLEMENTED = $20;
|
|
|
|
const
|
|
DEFCOLWIDTH = 64;
|
|
DEFBUTTONWIDTH = 25;
|
|
DEFIMAGEPADDING = 2;
|
|
DEFMINSIZE = 0;
|
|
DEFMAXSIZE = 0;
|
|
DEFSIZEPRIORITY = 1;
|
|
|
|
type
|
|
EGridException = class(Exception);
|
|
|
|
type
|
|
TGridOption = (
|
|
goFixedVertLine,
|
|
goFixedHorzLine,
|
|
goVertLine,
|
|
goHorzLine,
|
|
goRangeSelect,
|
|
goDrawFocusSelected,
|
|
goRowSizing,
|
|
goColSizing,
|
|
goRowMoving,
|
|
goColMoving,
|
|
goEditing,
|
|
goAutoAddRows,
|
|
goTabs,
|
|
goRowSelect,
|
|
goAlwaysShowEditor,
|
|
goThumbTracking,
|
|
goColSpanning, // Enable CellExtent calculation
|
|
goRelaxedRowSelect, // User can see focused cell on goRowSelect
|
|
goDblClickAutoSize, // Double-clicking column borders (on headers) resizes column
|
|
goSmoothScroll, // Switch scrolling mode (pixel scroll is by default)
|
|
goFixedRowNumbering,
|
|
goScrollKeepVisible, // Keeps focused cell visible while scrolling
|
|
goHeaderHotTracking, // Header cells change look when mouse is over them
|
|
goHeaderPushedLook, // Header cells looks pushed when clicked
|
|
goSelectionActive, // Setting of Selection moves also cell cursor
|
|
goFixedColSizing, // Allow to resize fixed columns
|
|
goDontScrollPartCell, // Clicking partially visible cells will not scroll
|
|
goCellHints, // Show individual cell hints
|
|
goTruncCellHints, // Show cell hints if cell text is too long
|
|
goCellEllipsis, // Show "..." if cell text is too long
|
|
goAutoAddRowsSkipContentCheck, //Also add a row (if AutoAddRows in Options) if last row is empty
|
|
goRowHighlight // Highlight the current row
|
|
);
|
|
TGridOptions = set of TGridOption;
|
|
|
|
TGridOption2 = (
|
|
goScrollToLastCol, // Allow scrolling to last column (so that last column can be LeftCol)
|
|
goScrollToLastRow, // Allow scrolling to last row (so that last row can be TopRow)
|
|
goEditorParentColor, // Set editor's ParentColor to True
|
|
goEditorParentFont // Set editor's ParentFont to True
|
|
);
|
|
TGridOptions2 = set of TGridOption2;
|
|
|
|
TGridSaveOptions = (
|
|
soDesign, // Save grid structure (col/row count and Options)
|
|
soAttributes, // Save grid attributes (Font,Brush,TextStyle)
|
|
soContent, // Save Grid Content (Text in StringGrid)
|
|
soPosition // Save Grid cursor and selection position
|
|
);
|
|
TSaveOptions = set of TGridSaveOptions;
|
|
|
|
TGridDrawState = set of (gdSelected, gdFocused, gdFixed, gdHot, gdPushed, gdRowHighlight);
|
|
TGridState =(gsNormal, gsSelecting, gsRowSizing, gsColSizing, gsRowMoving,
|
|
gsColMoving, gsHeaderClicking, gsButtonColumnClicking);
|
|
|
|
TGridZone = (gzNormal, gzFixedCols, gzFixedRows, gzFixedCells, gzInvalid);
|
|
TGridZoneSet = set of TGridZone;
|
|
|
|
TAutoAdvance = (aaNone,aaDown,aaRight,aaLeft, aaRightDown, aaLeftDown,
|
|
aaRightUp, aaLeftUp);
|
|
|
|
{ Option goRangeSelect: --> select a single range only, or multiple ranges }
|
|
TRangeSelectMode = (rsmSingle, rsmMulti);
|
|
|
|
TItemType = (itNormal,itCell,itColumn,itRow,itFixed,itFixedColumn,itFixedRow,itSelected);
|
|
|
|
TColumnButtonStyle = (
|
|
cbsAuto,
|
|
cbsEllipsis,
|
|
cbsNone,
|
|
cbsPickList,
|
|
cbsCheckboxColumn,
|
|
cbsButton,
|
|
cbsButtonColumn
|
|
);
|
|
|
|
TTitleStyle = (tsLazarus, tsStandard, tsNative);
|
|
|
|
TGridFlagsOption = (gfEditorUpdateLock, gfNeedsSelectActive, gfEditorTab,
|
|
gfRevEditorTab, gfVisualChange, gfColumnsLocked,
|
|
gfEditingDone, gfSizingStarted, gfPainting, gfUpdatingSize, gfClientRectChange,
|
|
gfAutoEditPending, gfUpdatingScrollbar);
|
|
TGridFlags = set of TGridFlagsOption;
|
|
|
|
TSortOrder = (soAscending, soDescending);
|
|
|
|
TPrefixOption = (poNone, poHeaderClick);
|
|
|
|
TMouseWheelOption = (mwCursor, mwGrid);
|
|
|
|
TCellHintPriority = (chpAll, chpAllNoDefault, chpTruncOnly);
|
|
// The grid can display three types of hint: the default hint (Hint property),
|
|
// individual cell hints (OnCellHint event), and hints for truncated cells.
|
|
// TCellHintPriority determines how the overall hint is combined when more
|
|
// multiple hint texts are to be displayed.
|
|
|
|
TCellProcessType = (cpCopy, cpPaste);
|
|
|
|
const
|
|
soAll: TSaveOptions = [soDesign, soAttributes, soContent, soPosition];
|
|
|
|
DefaultGridOptions = [goFixedVertLine, goFixedHorzLine,
|
|
goVertLine, goHorzLine, goRangeSelect, goSmoothScroll ];
|
|
DefaultGridOptions2 = [];
|
|
|
|
constRubberSpace: byte = 2;
|
|
constCellPadding: byte = 3;
|
|
constColRowBorderTolerance: byte = 3;
|
|
|
|
var
|
|
// Values to be used after HighDPI scaling
|
|
varRubberSpace: byte;
|
|
varCellpadding: byte;
|
|
varColRowBorderTolerance: byte;
|
|
|
|
type
|
|
|
|
TCustomGrid = class;
|
|
TGridColumn = class;
|
|
|
|
PCellProps= ^TCellProps;
|
|
TCellProps=record
|
|
Attr: pointer;
|
|
Data: TObject;
|
|
Text: pchar;
|
|
end;
|
|
|
|
PColRowProps= ^TColRowProps;
|
|
TColRowProps=record
|
|
Size: Integer;
|
|
FixedAttr: pointer;
|
|
NormalAttr: pointer;
|
|
end;
|
|
|
|
PGridMessage=^TGridMessage;
|
|
TGridMessage=record
|
|
LclMsg: TLMessage;
|
|
Grid: TCustomGrid;
|
|
Col,Row: Integer;
|
|
Value: string;
|
|
CellRect: TRect;
|
|
Options: Integer;
|
|
end;
|
|
|
|
type
|
|
{ Default cell editor for TStringGrid }
|
|
{ TStringCellEditor }
|
|
|
|
TStringCellEditor=class(TCustomMaskEdit)
|
|
private
|
|
FGrid: TCustomGrid;
|
|
FCol,FRow:Integer;
|
|
protected
|
|
procedure WndProc(var TheMessage : TLMessage); override;
|
|
procedure Change; override;
|
|
procedure KeyDown(var Key : Word; Shift : TShiftState); override;
|
|
procedure msg_SetMask(var Msg: TGridMessage); message GM_SETMASK;
|
|
procedure msg_SetValue(var Msg: TGridMessage); message GM_SETVALUE;
|
|
procedure msg_GetValue(var Msg: TGridMessage); message GM_GETVALUE;
|
|
procedure msg_SetGrid(var Msg: TGridMessage); message GM_SETGRID;
|
|
procedure msg_SelectAll(var Msg: TGridMessage); message GM_SELECTALL;
|
|
procedure msg_SetPos(var Msg: TGridMessage); message GM_SETPOS;
|
|
procedure msg_GetGrid(var Msg: TGridMessage); message GM_GETGRID;
|
|
public
|
|
constructor Create(Aowner : TComponent); override;
|
|
procedure EditingDone; override;
|
|
property EditText;
|
|
property OnEditingDone;
|
|
end;
|
|
|
|
{ TButtonCellEditor }
|
|
|
|
TButtonCellEditor = class(TButton)
|
|
private
|
|
FGrid: TCustomGrid;
|
|
FCol,FRow: Integer;
|
|
protected
|
|
procedure msg_SetGrid(var Msg: TGridMessage); message GM_SETGRID;
|
|
procedure msg_SetBounds(var Msg: TGridMessage); message GM_SETBOUNDS;
|
|
procedure msg_SetPos(var Msg: TGridMessage); message GM_SETPOS;
|
|
procedure msg_Ready(var Msg: TGridMessage); message GM_READY;
|
|
procedure msg_GetGrid(var Msg: TGridMessage); message GM_GETGRID;
|
|
public
|
|
property Col: Integer read FCol;
|
|
property Row: Integer read FRow;
|
|
end;
|
|
|
|
{ TPickListCellEditor }
|
|
|
|
TPickListCellEditor = class(TCustomComboBox)
|
|
private
|
|
FGrid: TCustomGrid;
|
|
FCol,FRow: Integer;
|
|
protected
|
|
procedure WndProc(var TheMessage : TLMessage); override;
|
|
procedure KeyDown(var Key : Word; Shift : TShiftState); override;
|
|
procedure DropDown; override;
|
|
procedure CloseUp; override;
|
|
procedure Select; override;
|
|
procedure Change; override;
|
|
procedure msg_GetValue(var Msg: TGridMessage); message GM_GETVALUE;
|
|
procedure msg_SetGrid(var Msg: TGridMessage); message GM_SETGRID;
|
|
procedure msg_SetValue(var Msg: TGridMessage); message GM_SETVALUE;
|
|
procedure msg_SetPos(var Msg: TGridMessage); message GM_SETPOS;
|
|
procedure msg_GetGrid(var Msg: TGridMessage); message GM_GETGRID;
|
|
public
|
|
procedure EditingDone; override;
|
|
property BorderStyle;
|
|
property OnEditingDone;
|
|
end;
|
|
|
|
{ TCompositeCellEditor }
|
|
|
|
TEditorItem = record
|
|
Editor: TWinControl;
|
|
Align: TAlign;
|
|
ActiveControl: boolean;
|
|
end;
|
|
|
|
TCompositeCellEditor = class(TCustomControl)
|
|
private
|
|
FGrid: TCustomGrid;
|
|
FCol,FRow: Integer;
|
|
FEditors: array of TEditorItem;
|
|
procedure DispatchMsg(msg: TGridMessage);
|
|
function GetMaxLength: Integer;
|
|
procedure SetMaxLength(AValue: Integer);
|
|
protected
|
|
function DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean; override;
|
|
procedure msg_GetValue(var Msg: TGridMessage); message GM_GETVALUE;
|
|
procedure msg_SetGrid(var Msg: TGridMessage); message GM_SETGRID;
|
|
procedure msg_SetValue(var Msg: TGridMessage); message GM_SETVALUE;
|
|
procedure msg_SetBounds(var Msg: TGridMessage); message GM_SETBOUNDS;
|
|
procedure msg_SetMask(var Msg: TGridMessage); message GM_SETMASK;
|
|
procedure msg_SelectAll(var Msg: TGridMessage); message GM_SELECTALL;
|
|
procedure CMControlChange(var Message: TLMEssage); message CM_CONTROLCHANGE;
|
|
procedure msg_SetPos(var Msg: TGridMessage); message GM_SETPOS;
|
|
procedure msg_GetGrid(var Msg: TGridMessage); message GM_GETGRID;
|
|
function GetActiveControl: TWinControl;
|
|
procedure VisibleChanging; override;
|
|
function SendChar(AChar: TUTF8Char): Integer;
|
|
procedure SetColor(Value: TColor); override;
|
|
procedure WndProc(var TheMessage : TLMessage); override;
|
|
procedure CustomAlignPosition(AControl: TControl; var ANewLeft, ANewTop, ANewWidth,
|
|
ANewHeight: Integer; var AlignRect: TRect; AlignInfo: TAlignInfo); override;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure AddEditor(aEditor: TWinControl; aAlign: TAlign; ActiveCtrl:boolean);
|
|
procedure SetFocus; override;
|
|
function Focused: Boolean; override;
|
|
property MaxLength: Integer read GetMaxLength write SetMaxLength;
|
|
property ActiveControl: TWinControl read GetActiveControl;
|
|
end;
|
|
|
|
|
|
TOnDrawCell =
|
|
procedure(Sender: TObject; aCol, aRow: Integer; aRect: TRect;
|
|
aState:TGridDrawState) of object;
|
|
|
|
TOnSelectCellEvent =
|
|
procedure(Sender: TObject; aCol, aRow: Integer;
|
|
var CanSelect: Boolean) of object;
|
|
|
|
TOnSelectEvent =
|
|
procedure(Sender: TObject; aCol, aRow: Integer) of object;
|
|
|
|
TGridOperationEvent =
|
|
procedure (Sender: TObject; IsColumn:Boolean;
|
|
sIndex, tIndex: Integer) of object;
|
|
|
|
THdrEvent =
|
|
procedure(Sender: TObject; IsColumn: Boolean; Index: Integer) of object;
|
|
|
|
TOnCompareCells =
|
|
procedure (Sender: TObject; ACol, ARow, BCol,BRow: Integer;
|
|
var Result: integer) of object;
|
|
|
|
TSelectEditorEvent =
|
|
procedure(Sender: TObject; aCol, aRow: Integer;
|
|
var Editor: TWinControl) of object;
|
|
|
|
TOnPrepareCanvasEvent =
|
|
procedure(Sender: TObject; aCol, aRow: Integer;
|
|
aState: TGridDrawState) of object;
|
|
|
|
TUserCheckBoxBitmapEvent =
|
|
procedure(Sender: TObject; const aCol, aRow: Integer;
|
|
const CheckedState: TCheckboxState;
|
|
var ABitmap: TBitmap) of object;
|
|
|
|
TUserCheckBoxImageEvent =
|
|
procedure(Sender: TObject; const aCol, aRow: Integer;
|
|
const CheckedState: TCheckBoxState;
|
|
var ImageList: TCustomImageList;
|
|
var ImageIndex: TImageIndex) of object;
|
|
|
|
TValidateEntryEvent =
|
|
procedure(Sender: TObject; aCol, aRow: Integer;
|
|
const OldValue: string; var NewValue: String) of object;
|
|
|
|
TToggledCheckboxEvent = procedure(Sender: TObject; aCol, aRow: Integer;
|
|
aState: TCheckboxState) of object;
|
|
|
|
THeaderSizingEvent = procedure(Sender: TObject; const IsColumn: boolean;
|
|
const aIndex, aSize: Integer) of object;
|
|
|
|
TCellProcessEvent = procedure(Sender: TObject; aCol, aRow: Integer;
|
|
processType: TCellProcessType;
|
|
var aValue: string) of object;
|
|
|
|
TGetCellHintEvent = procedure (Sender: TObject; ACol, ARow: Integer;
|
|
var HintText: String) of object;
|
|
|
|
TSaveColumnEvent = procedure (Sender, aColumn: TObject; aColIndex: Integer;
|
|
aCfg: TXMLConfig; const aVersion: integer;
|
|
const aPath: string) of object;
|
|
|
|
{ TVirtualGrid }
|
|
|
|
TVirtualGrid=class
|
|
private
|
|
FColCount: Integer;
|
|
FRowCount: Integer;
|
|
FCellArr, FColArr, FRowArr: TPointerPointerArray;
|
|
function GetCells(Col, Row: Integer): PCellProps;
|
|
function GetRows(Row: Integer): PColRowProps;
|
|
function GetCols(Col: Integer): PColRowProps;
|
|
procedure SetCells(Col, Row: Integer; const AValue: PCellProps);
|
|
procedure SetRows(Row: Integer; const Avalue: PColRowProps);
|
|
procedure SetColCount(const Avalue: Integer);
|
|
procedure SetRowCount(const Avalue: Integer);
|
|
procedure SetCols(Col: Integer; const Avalue: PColRowProps);
|
|
protected
|
|
procedure doDestroyItem(Sender: TObject; Col,Row: Integer; var Item: Pointer);
|
|
procedure doNewItem(Sender: TObject; Col,Row: Integer; var Item: Pointer);
|
|
procedure DeleteColRow(IsColumn: Boolean; index: Integer);
|
|
procedure MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer);
|
|
procedure ExchangeColRow(IsColumn: Boolean; index, WithIndex: Integer);
|
|
procedure InsertColRow(IsColumn: Boolean; Index: Integer);
|
|
procedure DisposeCell(var P: PCellProps); virtual;
|
|
procedure DisposeColRow(var p: PColRowProps); virtual;
|
|
function IsColumnIndexValid(AIndex: Integer): boolean; inline;
|
|
function IsRowIndexValid(AIndex: Integer): boolean; inline;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
function GetDefaultCell: PcellProps;
|
|
function GetDefaultColRow: PColRowProps;
|
|
|
|
property ColCount: Integer read FColCount write SetColCount;
|
|
property RowCount: Integer read FRowCount write SetRowCount;
|
|
|
|
property Celda[Col,Row: Integer]: PCellProps read GetCells write SetCells;
|
|
property Cols[Col: Integer]: PColRowProps read GetCols write SetCols;
|
|
property Rows[Row: Integer]: PColRowProps read GetRows write SetRows;
|
|
end;
|
|
|
|
{ TGridColumnTitle }
|
|
|
|
TGridColumnTitle = class(TPersistent)
|
|
private
|
|
FColumn: TGridColumn;
|
|
FCaption: PChar;
|
|
FColor: ^TColor;
|
|
FAlignment: ^TAlignment;
|
|
FFont: TFont;
|
|
FImageIndex: TImageIndex;
|
|
FImageLayout: TButtonLayout;
|
|
FIsDefaultTitleFont: boolean;
|
|
FLayout: ^TTextLayout;
|
|
FPrefixOption: TPrefixOption;
|
|
FMultiline: Boolean;
|
|
FIsDefaultCaption: boolean;
|
|
procedure FontChanged(Sender: TObject);
|
|
function GetAlignment: TAlignment;
|
|
function GetColor: TColor;
|
|
function GetFont: TFont;
|
|
function GetLayout: TTextLayout;
|
|
function IsAlignmentStored: boolean;
|
|
function IsCaptionStored: boolean;
|
|
function IsColorStored: boolean;
|
|
function IsFontStored: boolean;
|
|
function IsLayoutStored: boolean;
|
|
procedure SetAlignment(const AValue: TAlignment);
|
|
procedure SetColor(const AValue: TColor);
|
|
procedure SetFont(const AValue: TFont);
|
|
procedure SetImageIndex(const AValue: TImageIndex);
|
|
procedure SetImageLayout(const AValue: TButtonLayout);
|
|
procedure SetLayout(const AValue: TTextLayout);
|
|
procedure SetMultiLine(const AValue: Boolean);
|
|
procedure SetPrefixOption(const AValue: TPrefixOption);
|
|
procedure WriteCaption(Writer: TWriter);
|
|
|
|
property IsDefaultFont: boolean read FIsDefaultTitleFont;
|
|
protected
|
|
function GetDefaultCaption: string; virtual;
|
|
function GetDefaultAlignment: TAlignment;
|
|
function GetDefaultColor: TColor;
|
|
function GetDefaultLayout: TTextLayout;
|
|
function GetOwner: TPersistent; override;
|
|
function GetCaption: TCaption;
|
|
procedure SetCaption(const AValue: TCaption); virtual;
|
|
procedure DefineProperties(Filer: TFiler); override;
|
|
public
|
|
constructor Create(TheColumn: TGridColumn); virtual;
|
|
destructor Destroy; override;
|
|
procedure Assign(Source: TPersistent); override;
|
|
procedure FillTitleDefaultFont;
|
|
procedure FixDesignFontsPPI(const ADesignTimePPI: Integer); virtual;
|
|
procedure ScaleFontsPPI(const AToPPI: Integer; const AProportion: Double); virtual;
|
|
function IsDefault: boolean;
|
|
property Column: TGridColumn read FColumn;
|
|
published
|
|
property Alignment: TAlignment read GetAlignment write SetAlignment stored IsAlignmentStored;
|
|
property Caption: TCaption read GetCaption write SetCaption stored IsCaptionStored;
|
|
property Color: TColor read GetColor write SetColor stored IsColorStored;
|
|
property Font: TFont read GetFont write SetFont stored IsFontStored;
|
|
property ImageIndex: TImageIndex read FImageIndex write SetImageIndex default -1;
|
|
property ImageLayout: TButtonLayout read FImageLayout write SetImageLayout default blGlyphRight;
|
|
property Layout: TTextLayout read GetLayout write SetLayout stored IsLayoutStored;
|
|
property MultiLine: Boolean read FMultiLine write SetMultiLine default false;
|
|
property PrefixOption: TPrefixOption read FPrefixOption write SetPrefixOption default poNone;
|
|
end;
|
|
|
|
{ TGridColumn }
|
|
|
|
TGridColumn = class(TCollectionItem)
|
|
private
|
|
FButtonStyle: TColumnButtonStyle;
|
|
FDropDownRows: Longint;
|
|
FTitle: TGridColumnTitle;
|
|
FWidthChanged: boolean;
|
|
FAlignment: ^TAlignment;
|
|
FColor: ^TColor;
|
|
FLayout: ^TTextLayout;
|
|
FVisible: ^Boolean;
|
|
FReadOnly: ^Boolean;
|
|
FWidth: ^Integer;
|
|
FFont: TFont;
|
|
FisDefaultFont: Boolean;
|
|
FPickList: TStrings;
|
|
FMinSize, FMaxSize, FSizePriority: ^Integer;
|
|
FValueChecked,FValueUnchecked: PChar;
|
|
FTag: PtrInt;
|
|
procedure FontChanged(Sender: TObject);
|
|
function GetAlignment: TAlignment;
|
|
function GetColor: TColor;
|
|
function GetExpanded: Boolean;
|
|
function GetFont: TFont;
|
|
function GetGrid: TCustomGrid;
|
|
function GetLayout: TTextLayout;
|
|
function GetMaxSize: Integer;
|
|
function GetMinSize: Integer;
|
|
function GetSizePriority: Integer;
|
|
function GetReadOnly: Boolean;
|
|
function GetStoredWidth: Integer;
|
|
function GetVisible: Boolean;
|
|
function GetWidth: Integer;
|
|
function IsAlignmentStored: boolean;
|
|
function IsColorStored: boolean;
|
|
function IsFontStored: boolean;
|
|
function IsLayoutStored: boolean;
|
|
function IsMinSizeStored: boolean;
|
|
function IsMaxSizeStored: boolean;
|
|
function IsReadOnlyStored: boolean;
|
|
function IsSizePriorityStored: boolean;
|
|
function IsValueCheckedStored: boolean;
|
|
function IsValueUncheckedStored: boolean;
|
|
function IsVisibleStored: boolean;
|
|
function IsWidthStored: boolean;
|
|
procedure SetAlignment(const AValue: TAlignment);
|
|
procedure SetButtonStyle(const AValue: TColumnButtonStyle);
|
|
procedure SetColor(const AValue: TColor);
|
|
procedure SetExpanded(const AValue: Boolean);
|
|
procedure SetFont(const AValue: TFont);
|
|
procedure SetLayout(const AValue: TTextLayout);
|
|
procedure SetMaxSize(const AValue: Integer);
|
|
procedure SetMinSize(const Avalue: Integer);
|
|
procedure SetPickList(const AValue: TStrings);
|
|
procedure SetReadOnly(const AValue: Boolean);
|
|
procedure SetSizePriority(const AValue: Integer);
|
|
procedure SetTitle(const AValue: TGridColumnTitle);
|
|
procedure SetValueChecked(const AValue: string);
|
|
procedure SetValueUnchecked(const AValue: string);
|
|
procedure SetVisible(const AValue: Boolean);
|
|
procedure SetWidth(const AValue: Integer);
|
|
protected
|
|
function GetDisplayName: string; override;
|
|
function GetDefaultAlignment: TAlignment; virtual;
|
|
function GetDefaultColor: TColor; virtual;
|
|
function GetDefaultLayout: TTextLayout; virtual;
|
|
function GetDefaultMaxSize: Integer; virtual;
|
|
function GetDefaultMinSize: Integer; virtual;
|
|
function GetDefaultReadOnly: boolean; virtual;
|
|
function GetDefaultSizePriority: Integer;
|
|
function GetDefaultVisible: boolean; virtual;
|
|
function GetDefaultValueChecked: string; virtual;
|
|
function GetDefaultValueUnchecked: string; virtual;
|
|
function GetDefaultWidth: Integer; virtual;
|
|
function GetPickList: TStrings; virtual;
|
|
function GetValueChecked: string;
|
|
function GetValueUnchecked: string;
|
|
procedure ColumnChanged; virtual;
|
|
procedure AllColumnsChange;
|
|
function CreateTitle: TGridColumnTitle; virtual;
|
|
procedure SetIndex(Value: Integer); override;
|
|
|
|
property IsDefaultFont: boolean read FIsDefaultFont;
|
|
public
|
|
constructor Create(ACollection: TCollection); override;
|
|
destructor Destroy; override;
|
|
procedure Assign(Source: TPersistent); override;
|
|
procedure FillDefaultFont;
|
|
procedure FixDesignFontsPPI(const ADesignTimePPI: Integer); virtual;
|
|
procedure ScaleFontsPPI(const AToPPI: Integer; const AProportion: Double); virtual;
|
|
function IsDefault: boolean; virtual;
|
|
property Grid: TCustomGrid read GetGrid;
|
|
property DefaultWidth: Integer read GetDefaultWidth;
|
|
property StoredWidth: Integer read GetStoredWidth;
|
|
property WidthChanged: boolean read FWidthChanged;
|
|
|
|
published
|
|
property Alignment: TAlignment read GetAlignment write SetAlignment stored IsAlignmentStored;
|
|
property ButtonStyle: TColumnButtonStyle read FButtonStyle write SetButtonStyle default cbsAuto;
|
|
property Color: TColor read GetColor write SetColor stored IsColorStored;
|
|
property DropDownRows: Longint read FDropDownRows write FDropDownRows default 7;
|
|
property Expanded: Boolean read GetExpanded write SetExpanded default True;
|
|
property Font: TFont read GetFont write SetFont stored IsFontStored;
|
|
property Layout: TTextLayout read GetLayout write SetLayout stored IsLayoutStored;
|
|
property MinSize: Integer read GetMinSize write SetMinSize stored IsMinSizeStored;
|
|
property MaxSize: Integer read GetMaxSize write SetMaxSize stored isMaxSizeStored;
|
|
property PickList: TStrings read GetPickList write SetPickList;
|
|
property ReadOnly: Boolean read GetReadOnly write SetReadOnly stored IsReadOnlyStored;
|
|
property SizePriority: Integer read GetSizePriority write SetSizePriority stored IsSizePriorityStored;
|
|
property Tag: PtrInt read FTag write FTag default 0;
|
|
property Title: TGridColumnTitle read FTitle write SetTitle;
|
|
property Width: Integer read GetWidth write SetWidth stored IsWidthStored;
|
|
property Visible: Boolean read GetVisible write SetVisible stored IsVisibleStored;
|
|
property ValueChecked: string read GetValueChecked write SetValueChecked
|
|
stored IsValueCheckedStored;
|
|
property ValueUnchecked: string read GetValueUnchecked write SetValueUnchecked
|
|
stored IsValueUncheckedStored;
|
|
end;
|
|
|
|
TGridPropertyBackup=record
|
|
ValidData: boolean;
|
|
FixedRowCount: Integer;
|
|
FixedColCount: Integer;
|
|
RowCount: Integer;
|
|
ColCount: Integer;
|
|
end;
|
|
|
|
{ TGridColumns }
|
|
|
|
TGridColumns = class(TCollection)
|
|
private
|
|
FGrid: TCustomGrid;
|
|
function GetColumn(Index: Integer): TGridColumn;
|
|
function GetEnabled: Boolean;
|
|
procedure SetColumn(Index: Integer; Value: TGridColumn);
|
|
function GetVisibleCount: Integer;
|
|
protected
|
|
function GetOwner: TPersistent; override;
|
|
procedure Update(Item: TCollectionItem); override;
|
|
procedure TitleFontChanged;
|
|
procedure FontChanged;
|
|
procedure RemoveColumn(Index: Integer);
|
|
procedure MoveColumn(FromIndex,ToIndex: Integer); virtual;
|
|
procedure ExchangeColumn(Index,WithIndex: Integer);
|
|
procedure InsertColumn(Index: Integer);
|
|
public
|
|
constructor Create(AGrid: TCustomGrid; aItemClass: TCollectionItemClass);
|
|
function Add: TGridColumn;
|
|
procedure Clear;
|
|
function ColumnByTitle(const aTitle: string): TGridColumn;
|
|
function RealIndex(Index: Integer): Integer;
|
|
function IndexOf(Column: TGridColumn): Integer;
|
|
function IsDefault: boolean;
|
|
function HasIndex(Index: Integer): boolean;
|
|
function VisibleIndex(Index: Integer): Integer;
|
|
property Grid: TCustomGrid read FGrid;
|
|
property Items[Index: Integer]: TGridColumn read GetColumn write SetColumn; default;
|
|
property VisibleCount: Integer read GetVisibleCount;
|
|
property Enabled: Boolean read GetEnabled;
|
|
end;
|
|
|
|
type
|
|
TGridCoord = TPoint;
|
|
TGridRect = TRect;
|
|
TGridRectArray = array of TGridRect;
|
|
|
|
TSizingRec = record
|
|
Index: Integer;
|
|
OffIni,OffEnd: Integer;
|
|
DeltaOff: Integer;
|
|
PrevLine: boolean;
|
|
PrevOffset: Integer;
|
|
end;
|
|
|
|
TGridDataCache=record
|
|
FixedWidth: Integer; // Sum( Fixed ColsWidths[i] )
|
|
FixedHeight: Integer; // Sum( Fixed RowsHeights[i] )
|
|
GridWidth: Integer; // Sum( ColWidths[i] )
|
|
GridHeight: Integer; // Sum( RowHeights[i] )
|
|
ClientWidth: Integer; // Width-VertScrollbar.Size
|
|
ClientHeight: Integer; // Height-HorzScrollbar.Size
|
|
ClientRect: TRect; // Cache for ClientRect - GetBorderWidth need for Bidi
|
|
ScrollWidth: Integer; // ClientWidth-FixedWidth
|
|
ScrollHeight: Integer; // ClientHeight-FixedHeight
|
|
HScrollBarNetRange: Integer;//ScrollBar Range-Page
|
|
VisibleGrid: TRect; // Visible non fixed rectangle of cellcoordinates
|
|
MaxClientXY: Tpoint; // VisibleGrid.BottomRight (pixel) coordinates
|
|
ValidRows: boolean; // true if there are not fixed columns to show
|
|
ValidCols: boolean; // true if there are not fixed rows to show
|
|
ValidGrid: boolean; // true if there are not fixed cells to show
|
|
AccumWidth: TIntegerList; // Accumulated width per column
|
|
AccumHeight: TIntegerList; // Accumulated Height per row
|
|
TLColOff,TLRowOff: Integer; // TopLeft Offset in pixels
|
|
MaxTopLeft: TPoint; // Max Top left ( cell coorditates)
|
|
MaxTLOffset: TPoint; // Max Top left offset of the last cell
|
|
HotCell: TPoint; // currently hot cell
|
|
HotCellPainted: boolean; // HotCell was already painter?
|
|
HotGridZone: TGridZone; // GridZone of last MouseMove
|
|
ClickCell: TPoint; // Cell coords of the latest mouse click
|
|
ClickMouse: TPoint; // mouse coords of the latest mouse click
|
|
PushedCell: TPoint; // Cell coords of cell being pushed
|
|
PushedMouse: TPoint; // mouse Coords of the cell being pushed
|
|
ClickCellPushed: boolean; // Header Cell is currently pushed?
|
|
FullVisibleGrid: TRect; // visible cells excluding partially visible cells
|
|
MouseCell: TPoint; // Cell which contains the mouse
|
|
OldMaxTopLeft: TPoint; // previous MaxTopleft (before col sizing)
|
|
end;
|
|
|
|
TGridCursorState = (gcsDefault, gcsColWidthChanging, gcsRowHeightChanging, gcsDragging);
|
|
|
|
TGridScrollerDoScroll = procedure (Dir: TPoint) of object;
|
|
|
|
{ TGridScroller }
|
|
|
|
TGridScroller = class
|
|
private
|
|
Dir: TPoint;
|
|
Timer: TTimer;
|
|
Callback: TGridScrollerDoScroll;
|
|
procedure TimerTick(Sender: TObject);
|
|
public
|
|
constructor Create(DoScroll: TGridScrollerDoScroll);
|
|
destructor Destroy; override;
|
|
procedure Start(ADir: TPoint);
|
|
end;
|
|
|
|
type
|
|
|
|
{ TCustomGrid }
|
|
|
|
TCustomGrid=class(TCustomControl)
|
|
private
|
|
FAlternateColor: TColor;
|
|
FAutoAdvance: TAutoAdvance;
|
|
FAutoEdit: boolean;
|
|
FAutoFillColumns: boolean;
|
|
FBorderColor: TColor;
|
|
FDefaultDrawing: Boolean;
|
|
FEditor: TWinControl;
|
|
FEditorHidingCount: Integer;
|
|
FEditorMode: Boolean;
|
|
FEditorOldValue: string;
|
|
FEditorShowing: Boolean;
|
|
FEditorKey: Boolean;
|
|
FEditorOptions: Integer;
|
|
FExtendedSelect: boolean;
|
|
FFastEditing: boolean;
|
|
FAltColorStartNormal: boolean;
|
|
FFlat: Boolean;
|
|
FOnAfterSelection: TOnSelectEvent;
|
|
FOnLoadColumn: TSaveColumnEvent;
|
|
FOnSaveColumn: TSaveColumnEvent;
|
|
FRangeSelectMode: TRangeSelectMode;
|
|
FSelections: TGridRectArray;
|
|
FOnUserCheckboxBitmap: TUserCheckboxBitmapEvent;
|
|
FOnUserCheckboxImage: TUserCheckBoxImageEvent;
|
|
FSortOrder: TSortOrder;
|
|
FSortColumn: Integer;
|
|
FSortLCLImages: TLCLGlyphs;
|
|
FTabAdvance: TAutoAdvance;
|
|
FTitleImageList: TImageList;
|
|
FTitleImageListWidth: Integer;
|
|
FTitleStyle: TTitleStyle;
|
|
FAscImgInd: TImageIndex;
|
|
FDescImgInd: TImageIndex;
|
|
FOnCompareCells: TOnCompareCells;
|
|
FGridLineStyle: TPenStyle;
|
|
FGridLineWidth: Integer;
|
|
FDefColWidth, FDefRowHeight: Integer;
|
|
FRealizedDefColWidth, FRealizedDefRowHeight: Integer;
|
|
FCol,FRow, FFixedCols, FFixedRows: Integer;
|
|
FOnEditButtonClick: TNotifyEvent;
|
|
FOnButtonClick: TOnSelectEvent;
|
|
FOnPickListSelect: TNotifyEvent;
|
|
FOnCheckboxToggled: TToggledCheckboxEvent;
|
|
FOnPrepareCanvas: TOnPrepareCanvasEvent;
|
|
FOnSelectEditor: TSelectEditorEvent;
|
|
FOnValidateEntry: TValidateEntryEvent;
|
|
FGridLineColor, FFixedGridLineColor: TColor;
|
|
FFixedColor, FFixedHotColor, FFocusColor, FSelectedColor: TColor;
|
|
FDisabledFontColor: TColor;
|
|
FFadeUnfocusedSelection: boolean;
|
|
FFocusRectVisible: boolean;
|
|
FCols,FRows: TIntegerList;
|
|
FsaveOptions: TSaveOptions;
|
|
FScrollBars: TScrollStyle;
|
|
FSelectActive: Boolean;
|
|
FTopLeft: TPoint;
|
|
FPivot: TPoint;
|
|
FRange: TRect;
|
|
FDragDx: Integer;
|
|
FMoveLast: TPoint;
|
|
FUpdateCount: Integer;
|
|
FGCache: TGridDataCache;
|
|
FOptions: TGridOptions;
|
|
FOptions2: TGridOptions2;
|
|
FOnDrawCell: TOnDrawcell;
|
|
FOnBeforeSelection: TOnSelectEvent;
|
|
FOnSelection: TOnSelectEvent;
|
|
FOnTopLeftChanged: TNotifyEvent;
|
|
FUseXORFeatures: boolean;
|
|
FValidateOnSetSelection: boolean;
|
|
FVSbVisible, FHSbVisible: ShortInt; // state: -1 not initialized, 0 hidden, 1 visible
|
|
FDefaultTextStyle: TTextStyle;
|
|
FLastWidth: Integer;
|
|
FTitleFont, FLastFont: TFont;
|
|
FTitleFontIsDefault: boolean;
|
|
FColumns: TGridColumns;
|
|
FButtonEditor: TButtonCellEditor;
|
|
FStringEditor: TStringCellEditor;
|
|
FButtonStringEditor: TCompositeCellEditor;
|
|
FPickListEditor: TPickListCellEditor;
|
|
FExtendedColSizing: boolean;
|
|
FExtendedRowSizing: boolean;
|
|
FUpdatingAutoFillCols: boolean;
|
|
FGridBorderStyle: TBorderStyle;
|
|
FGridFlags: TGridFlags;
|
|
FGridPropBackup: TGridPropertyBackup;
|
|
FStrictSort: boolean;
|
|
FIgnoreClick: boolean;
|
|
FAllowOutboundEvents: boolean;
|
|
FColumnClickSorts: boolean;
|
|
FHeaderHotZones: TGridZoneSet;
|
|
FHeaderPushZones: TGridZoneSet;
|
|
FCursorChangeLock: Integer;
|
|
FCursorState: TGridCursorState;
|
|
FColRowDragIndicatorColor: TColor;
|
|
FSavedCursor: TCursor;
|
|
FSpecialCursors: array[gcsColWidthChanging..gcsDragging] of TCursor;
|
|
FSizing: TSizingRec;
|
|
FRowAutoInserted: Boolean;
|
|
FMouseWheelOption: TMouseWheelOption;
|
|
FSavedHint: String;
|
|
FCellHintPriority: TCellHintPriority;
|
|
FOnGetCellHint: TGetCellHintEvent;
|
|
FScroller: TGridScroller;
|
|
procedure AdjustCount(IsColumn:Boolean; OldValue, NewValue:Integer);
|
|
procedure CacheVisibleGrid;
|
|
procedure CancelSelection;
|
|
procedure CheckFixedCount(aCol,aRow,aFCol,aFRow: Integer);
|
|
procedure CheckCount(aNewColCount, aNewRowCount: Integer; FixEditor: boolean=true);
|
|
procedure CheckIndex(IsColumn: Boolean; Index: Integer);
|
|
function CheckTopLeft(aCol,aRow: Integer; CheckCols,CheckRows: boolean): boolean;
|
|
function GetQuickColRow: TPoint;
|
|
procedure SetQuickColRow(AValue: TPoint);
|
|
function IsCellButtonColumn(ACell: TPoint): boolean;
|
|
function GetSelectedColumn: TGridColumn;
|
|
procedure SetAlternateColor(const AValue: TColor);
|
|
procedure SetAutoFillColumns(const AValue: boolean);
|
|
procedure SetBorderColor(const AValue: TColor);
|
|
procedure SetColumnClickSorts(const AValue: boolean);
|
|
procedure SetColumns(const AValue: TGridColumns);
|
|
procedure SetEditorOptions(const AValue: Integer);
|
|
procedure SetEditorBorderStyle(const AValue: TBorderStyle);
|
|
procedure SetAltColorStartNormal(const AValue: boolean);
|
|
procedure SetFlat(const AValue: Boolean);
|
|
procedure SetFocusRectVisible(const AValue: Boolean);
|
|
procedure ScrollerDoScroll(Dir: TPoint);
|
|
procedure SetScroller(Dir: TPoint);
|
|
procedure SetTitleImageList(const AValue: TImageList);
|
|
procedure SetTitleImageListWidth(const aTitleImageListWidth: Integer);
|
|
procedure SetTitleFont(const AValue: TFont);
|
|
procedure SetTitleStyle(const AValue: TTitleStyle);
|
|
procedure SetUseXorFeatures(const AValue: boolean);
|
|
function doColSizing(X,Y: Integer): Boolean;
|
|
function doRowSizing(X,Y: Integer): Boolean;
|
|
procedure doColMoving(X,Y: Integer);
|
|
procedure doPushCell;
|
|
procedure doRowMoving(X,Y: Integer);
|
|
procedure doTopleftChange(DimChg: Boolean);
|
|
procedure DrawXORVertLine(X: Integer);
|
|
procedure DrawXORHorzLine(Y: Integer);
|
|
function EditorGetValue(validate:boolean=false): boolean;
|
|
procedure EditorPos;
|
|
procedure EditorShowChar(Ch: TUTF8Char);
|
|
procedure EditorSetMode(const AValue: Boolean);
|
|
procedure EditorSetValue;
|
|
function EditorAlwaysShown: Boolean;
|
|
procedure FixPosition(IsColumn: Boolean; aIndex: Integer);
|
|
procedure FixScroll;
|
|
function GetLeftCol: Integer;
|
|
function GetColCount: Integer;
|
|
function GetColWidths(Acol: Integer): Integer;
|
|
function GetColumns: TGridColumns;
|
|
function GetDefColWidth: Integer;
|
|
function GetDefRowHeight: Integer;
|
|
function GetEditorBorderStyle: TBorderStyle;
|
|
function GetBorderWidth: Integer;
|
|
procedure GetTitleImageInfo(aColumnIndex:Integer; out ImgIndex: Integer; out ImgLayout: TButtonLayout);
|
|
procedure GetSortTitleImageInfo(aColumnIndex:Integer; out ImgList: TCustomImageList;
|
|
out ImgIndex, ImgListWidth: Integer; out NativeSortGlyphs: Boolean);
|
|
function GetRowCount: Integer;
|
|
function GetRowHeights(Arow: Integer): Integer;
|
|
function GetSelectedRange(AIndex: Integer): TGridRect;
|
|
function GetSelectedRangeCount: Integer;
|
|
function GetSelection: TGridRect;
|
|
function GetSpecialCursor(ACursorState: TGridCursorState): TCursor;
|
|
function GetTopRow: Longint;
|
|
function GetVisibleColCount: Integer;
|
|
function GetVisibleGrid: TRect;
|
|
function GetVisibleRowCount: Integer;
|
|
procedure HeadersMouseMove(const X,Y:Integer);
|
|
procedure InternalAutoFillColumns;
|
|
function InternalNeedBorder: boolean;
|
|
procedure InternalSetColWidths(aCol,aValue: Integer);
|
|
procedure InternalUpdateColumnWidths;
|
|
procedure InvalidateMovement(DCol,DRow: Integer; OldRange: TRect);
|
|
function IsAltColorStored: boolean;
|
|
function IsColumnsStored: boolean;
|
|
function IsPushCellActive: boolean;
|
|
procedure LoadColumns(cfg: TXMLConfig; Version: integer);
|
|
function LoadResBitmapImage(const ResName: string): TBitmap;
|
|
procedure LoadSub(ACfg: TXMLConfig);
|
|
procedure OnTitleFontChanged(Sender: TObject);
|
|
procedure ReadColumns(Reader: TReader);
|
|
procedure ReadColWidths(Reader: TReader);
|
|
procedure ReadRowHeights(Reader: TReader);
|
|
procedure ResetHotCell;
|
|
procedure ResetPushedCell(ResetColRow: boolean=True);
|
|
procedure RestoreCursor;
|
|
procedure SaveColumns(cfg: TXMLConfig; Version: integer);
|
|
function ScrollToCell(const aCol,aRow: Integer; const ForceFullyVisible: Boolean = True): Boolean;
|
|
function ScrollGrid(Relative:Boolean; DCol,DRow: Integer): TPoint;
|
|
procedure SetCol(AValue: Integer);
|
|
procedure SetColWidths(Acol: Integer; Avalue: Integer);
|
|
procedure SetColRowDragIndicatorColor(const AValue: TColor);
|
|
procedure SetDefColWidth(AValue: Integer);
|
|
procedure SetDefRowHeight(AValue: Integer);
|
|
procedure SetDefaultDrawing(const AValue: Boolean);
|
|
procedure SetEditor(AValue: TWinControl);
|
|
procedure SetFocusColor(const AValue: TColor);
|
|
procedure SetGridLineColor(const AValue: TColor);
|
|
procedure SetFixedGridLineColor(const AValue: TColor);
|
|
procedure SetGridLineStyle(const AValue: TPenStyle);
|
|
procedure SetGridLineWidth(const AValue: Integer);
|
|
procedure SetLeftCol(const AValue: Integer);
|
|
procedure SetOptions(const AValue: TGridOptions);
|
|
procedure SetOptions2(const AValue: TGridOptions2);
|
|
procedure SetRangeSelectMode(const AValue: TRangeSelectMode);
|
|
procedure SetRow(AValue: Integer);
|
|
procedure SetRowCount(AValue: Integer);
|
|
procedure SetRowHeights(Arow: Integer; Avalue: Integer);
|
|
procedure SetScrollBars(const AValue: TScrollStyle);
|
|
procedure SetSelectActive(const AValue: Boolean);
|
|
procedure SetSelection(const AValue: TGridRect);
|
|
procedure SetSpecialCursor(ACursorState: TGridCursorState; const AValue: TCursor);
|
|
procedure SetTopRow(const AValue: Integer);
|
|
function StartColSizing(const X, Y: Integer): boolean;
|
|
procedure ChangeCursor(ACursor: TCursor; ASaveCurrentCursor: Boolean = true);
|
|
function TitleFontIsStored: Boolean;
|
|
function TrySmoothScrollBy(aColDelta, aRowDelta: Integer): Boolean;
|
|
procedure TryScrollTo(aCol,aRow: Integer; ClearColOff, ClearRowOff: Boolean);
|
|
procedure UpdateCachedSizes;
|
|
procedure UpdateSBVisibility;
|
|
procedure UpdateSizes;
|
|
procedure WriteColumns(Writer: TWriter);
|
|
procedure WriteColWidths(Writer: TWriter);
|
|
procedure WriteRowHeights(Writer: TWriter);
|
|
procedure WMEraseBkgnd(var message: TLMEraseBkgnd); message LM_ERASEBKGND;
|
|
procedure WMGetDlgCode(var Msg: TLMNoParams); message LM_GETDLGCODE;
|
|
protected
|
|
fGridState: TGridState;
|
|
function RTLSign: Integer;
|
|
class procedure WSRegisterClass; override;
|
|
procedure AddSelectedRange;
|
|
procedure AdjustClientRect(var ARect: TRect); override;
|
|
procedure AdjustEditorBounds(NewCol,NewRow:Integer); virtual;
|
|
procedure AfterMoveSelection(const prevCol,prevRow: Integer); virtual;
|
|
procedure AssignTo(Dest: TPersistent); override;
|
|
procedure AutoAdjustColumn(aCol: Integer); virtual;
|
|
procedure BeforeMoveSelection(const DCol,DRow: Integer); virtual;
|
|
procedure BeginAutoDrag; override;
|
|
function BoxRect(ALeft,ATop,ARight,ABottom: Longint): TRect;
|
|
procedure CacheMouseDown(const X,Y:Integer);
|
|
procedure CalcAutoSizeColumn(const Index: Integer; var AMin,AMax,APriority: Integer); virtual;
|
|
procedure CalcCellExtent(acol, aRow: Integer; var aRect: TRect); overload; virtual; deprecated 'old function';
|
|
procedure CalcFocusRect(var ARect: TRect; adjust: boolean = true);
|
|
procedure CalcMaxTopLeft;
|
|
procedure CalcScrollbarsRange;
|
|
procedure CalculatePreferredSize(var PreferredWidth,
|
|
PreferredHeight: integer; WithThemeSpace: Boolean); override;
|
|
function CanEditShow: Boolean; virtual;
|
|
function CanGridAcceptKey(Key: Word; Shift: TShiftState): Boolean; virtual;
|
|
procedure CellClick(const aCol,aRow: Integer; const Button:TMouseButton); virtual;
|
|
procedure CellExtent(const aCol,aRow: Integer; var R: TRect; out exCol:Integer);
|
|
procedure CheckLimits(var aCol,aRow: Integer);
|
|
procedure CheckLimitsWithError(const aCol, aRow: Integer);
|
|
procedure CMBiDiModeChanged(var Message: TLMessage); message CM_BIDIMODECHANGED;
|
|
procedure CMMouseEnter(var Message: TLMessage); message CM_MOUSEENTER;
|
|
procedure CMMouseLeave(var Message :TLMessage); message CM_MouseLeave;
|
|
procedure ColRowDeleted(IsColumn: Boolean; index: Integer); virtual;
|
|
procedure ColRowExchanged(IsColumn: Boolean; index,WithIndex: Integer); virtual;
|
|
procedure ColRowInserted(IsColumn: boolean; index: integer); virtual;
|
|
procedure ColRowMoved(IsColumn: Boolean; FromIndex,ToIndex: Integer); virtual;
|
|
function ColRowToOffset(IsCol, Relative: Boolean; Index:Integer;
|
|
out StartPos, EndPos: Integer): Boolean;
|
|
function ColumnIndexFromGridColumn(Column: Integer): Integer;
|
|
function ColumnFromGridColumn(Column: Integer): TGridColumn;
|
|
procedure ColumnsChanged(aColumn: TGridColumn);
|
|
procedure ColWidthsChanged; virtual;
|
|
function CreateColumns: TGridColumns; virtual;
|
|
procedure CheckNewCachedSizes(var AGCache:TGridDataCache); virtual;
|
|
procedure CreateWnd; override;
|
|
procedure CreateParams(var Params: TCreateParams); override;
|
|
procedure Click; override;
|
|
procedure DblClick; override;
|
|
function DefaultColWidthIsStored: Boolean;
|
|
function DefaultRowHeightIsStored: Boolean;
|
|
procedure DefineProperties(Filer: TFiler); override;
|
|
procedure DestroyHandle; override;
|
|
function DialogChar(var Message: TLMKey): boolean; override;
|
|
function DoCompareCells(Acol,ARow,Bcol,BRow: Integer): Integer; virtual;
|
|
procedure DoCopyToClipboard; virtual;
|
|
procedure DoCutToClipboard; virtual;
|
|
procedure DoEditButtonClick(const ACol,ARow: Integer); virtual;
|
|
procedure DoEditorHide; virtual;
|
|
procedure DoEditorShow; virtual;
|
|
procedure DoExit; override;
|
|
procedure DoEnter; override;
|
|
procedure DoLoadColumn(Sender: TCustomGrid; aColumn: TGridColumn; aColIndex: Integer;
|
|
aCfg: TXMLConfig; aVersion: Integer; aPath: string); virtual;
|
|
procedure DoSaveColumn(Sender: TCustomGrid; aColumn: TGridColumn; aColIndex: Integer;
|
|
aCfg: TXMLConfig; aVersion: Integer; aPath: string); virtual;
|
|
function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; override;
|
|
function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
|
function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
|
function DoMouseWheelLeft(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
|
function DoMouseWheelRight(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
|
procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
|
|
const AXProportion, AYProportion: Double); override;
|
|
procedure DoOnChangeBounds; override;
|
|
procedure DoOPDeleteColRow(IsColumn: Boolean; index: Integer);
|
|
procedure DoOPExchangeColRow(IsColumn: Boolean; index, WithIndex: Integer);
|
|
procedure DoOPInsertColRow(IsColumn: boolean; index: integer);
|
|
procedure DoOPMoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer);
|
|
procedure DoPasteFromClipboard; virtual;
|
|
procedure DoPrepareCanvas(aCol,aRow:Integer; aState: TGridDrawState); virtual;
|
|
procedure DoOnResize; override;
|
|
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;
|
|
function DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean; override;
|
|
procedure DrawBorder;
|
|
procedure DrawAllRows; virtual;
|
|
procedure DrawFillRect(aCanvas:TCanvas; R:TRect);// Use FillRect after calc the new rect depened on Right To Left
|
|
procedure DrawCell(aCol,aRow:Integer; aRect:TRect; aState:TGridDrawState); virtual;
|
|
procedure DrawCellGrid(aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState); virtual;
|
|
procedure DrawTextInCell(aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState); virtual;
|
|
procedure DrawThemedCell(aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState);
|
|
procedure DrawCellText(aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState; aText: String); virtual;
|
|
procedure DrawGridCheckboxBitmaps(const aCol,aRow: Integer; const aRect: TRect;
|
|
const aState: TCheckboxState); virtual;
|
|
procedure DrawButtonCell(const aCol,aRow: Integer; aRect: TRect; const aState:TGridDrawState);
|
|
procedure DrawColRowMoving;
|
|
procedure DrawColumnText(aCol,aRow: Integer; aRect: TRect; aState:TGridDrawState); virtual;
|
|
procedure DrawColumnTitleImage(var ARect: TRect; AColumnIndex: Integer);
|
|
procedure DrawEdges;
|
|
procedure DrawFocusRect(aCol,aRow:Integer; ARect:TRect); virtual;
|
|
procedure DrawRow(aRow: Integer); virtual;
|
|
procedure EditButtonClicked(Sender: TObject);
|
|
procedure EditordoGetValue; virtual;
|
|
procedure EditordoResetValue; virtual;
|
|
procedure EditordoSetValue; virtual;
|
|
function EditorCanAcceptKey(const ch: TUTF8Char): boolean; virtual;
|
|
function EditorIsReadOnly: boolean; virtual;
|
|
procedure EditorHide; virtual;
|
|
function EditorLocked: boolean;
|
|
Function EditingAllowed(ACol : Integer = -1) : Boolean; virtual; // Returns true if grid and current column allow editing
|
|
procedure EditorSelectAll;
|
|
procedure EditorShow(const SelAll: boolean); virtual;
|
|
procedure EditorShowInCell(const aCol,aRow:Integer); virtual;
|
|
procedure EditorWidthChanged(aCol,aWidth: Integer); virtual;
|
|
function FirstGridColumn: integer; virtual;
|
|
function FixedGrid: boolean;
|
|
procedure FontChanged(Sender: TObject); override;
|
|
procedure GetAutoFillColumnInfo(const Index: Integer; var aMin,aMax,aPriority: Integer); virtual;
|
|
function GetCellHintText(ACol, ARow: Integer): string; virtual;
|
|
function GetCells(ACol, ARow: Integer): string; virtual;
|
|
function GetColumnAlignment(Column: Integer; ForTitle: Boolean): TAlignment;
|
|
function GetColumnColor(Column: Integer; ForTitle: Boolean): TColor;
|
|
function GetColumnFont(Column: Integer; ForTitle: Boolean): TFont;
|
|
function GetColumnLayout(Column: Integer; ForTitle: boolean): TTextLayout;
|
|
function GetColumnReadonly(Column: Integer): boolean;
|
|
function GetColumnTitle(Column: Integer): string;
|
|
function GetColumnWidth(Column: Integer): Integer;
|
|
function GetDeltaMoveNext(const Inverse: boolean; var ACol,ARow: Integer; const AAutoAdvance: TAutoAdvance): boolean; virtual;
|
|
function GetDefaultColumnAlignment(Column: Integer): TAlignment; virtual;
|
|
function GetDefaultColumnWidth(Column: Integer): Integer; virtual;
|
|
function GetDefaultColumnLayout(Column: Integer): TTextLayout; virtual;
|
|
function GetDefaultColumnReadOnly(Column: Integer): boolean; virtual;
|
|
function GetDefaultColumnTitle(Column: Integer): string; virtual;
|
|
function GetDefaultEditor(Column: Integer): TWinControl; virtual;
|
|
function GetDefaultRowHeight: integer; virtual;
|
|
function GetGridDrawState(ACol, ARow: Integer): TGridDrawState;
|
|
procedure GetImageForCheckBox(const aCol,aRow: Integer;
|
|
CheckBoxView: TCheckBoxState; var ImageList: TCustomImageList;
|
|
var ImageIndex: TImageIndex; var Bitmap: TBitmap); virtual;
|
|
function GetScrollBarPosition(Which: integer): Integer;
|
|
function GetSmoothScroll(Which: Integer): Boolean; virtual;
|
|
procedure GetSBVisibility(out HsbVisible,VsbVisible:boolean);virtual;
|
|
procedure GetSBRanges(const HsbVisible,VsbVisible: boolean;
|
|
out HsbRange,VsbRange,HsbPage,VsbPage,HsbPos,VsbPos:Integer); virtual;
|
|
procedure GetSelectedState(AState: TGridDrawState; out IsSelected:boolean); virtual;
|
|
function GetEditMask(ACol, ARow: Longint): string; virtual;
|
|
function GetEditText(ACol, ARow: Longint): string; virtual;
|
|
function GetFixedcolor: TColor; virtual;
|
|
function GetFirstVisibleColumn: Integer;
|
|
function GetFirstVisibleRow: Integer;
|
|
function GetLastVisibleColumn: Integer;
|
|
function GetLastVisibleRow: Integer;
|
|
function GetSelectedColor: TColor; virtual;
|
|
function GetTitleShowPrefix(Column: Integer): boolean;
|
|
function GetPxTopLeft: TPoint;
|
|
function GetTruncCellHintText(ACol, ARow: Integer): string; virtual;
|
|
function GridColumnFromColumnIndex(ColumnIndex: Integer): Integer;
|
|
procedure GridMouseWheel(Shift: TShiftState; Delta: Integer); virtual;
|
|
procedure HeaderClick(IsColumn: Boolean; index: Integer); virtual;
|
|
procedure HeaderSized(IsColumn: Boolean; index: Integer); virtual;
|
|
procedure HeaderSizing(const IsColumn:boolean; const AIndex,ASize:Integer); virtual;
|
|
procedure HideCellHintWindow;
|
|
procedure InternalSetColCount(ACount: Integer);
|
|
procedure InvalidateCell(aCol, aRow: Integer; Redraw: Boolean); overload;
|
|
procedure InvalidateFromCol(ACol: Integer);
|
|
procedure InvalidateGrid;
|
|
procedure InvalidateFocused;
|
|
function IsColumnIndexValid(AIndex: Integer): boolean;
|
|
function IsRowIndexValid(AIndex: Integer): boolean;
|
|
function IsColumnIndexVariable(AIndex: Integer): boolean;
|
|
function IsRowIndexVariable(AIndex: Integer): boolean;
|
|
function GetIsCellTitle(aCol,aRow: Integer): boolean; virtual;
|
|
function GetIsCellSelected(aCol, aRow: Integer): boolean; virtual;
|
|
function IsEmptyRow(ARow: Integer): Boolean;
|
|
function IsMouseOverCellButton(X,Y: Integer): boolean;
|
|
procedure KeyDown(var Key : Word; Shift : TShiftState); override;
|
|
procedure KeyUp(var Key : Word; Shift : TShiftState); override;
|
|
procedure KeyPress(var Key: char); override;
|
|
procedure LoadContent(cfg: TXMLConfig; Version: Integer); virtual;
|
|
procedure LoadGridOptions(cfg: TXMLConfig; Version: Integer); virtual;
|
|
procedure Loaded; override;
|
|
procedure LockEditor;
|
|
function MouseButtonAllowed(Button: TMouseButton): boolean; virtual;
|
|
procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
|
|
procedure MouseMove(Shift: TShiftState; X,Y: Integer); override;
|
|
procedure MouseUp(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
|
|
function MoveExtend(Relative: Boolean; DCol, DRow: Integer; ForceFullyVisible: Boolean = True): Boolean;
|
|
function MoveNextAuto(const Inverse: boolean): boolean;
|
|
function MoveNextSelectable(Relative:Boolean; DCol, DRow: Integer): Boolean; virtual;
|
|
procedure MoveSelection; virtual;
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
|
function OffsetToColRow(IsCol, Physical: Boolean; Offset: Integer;
|
|
out Index, Rest: Integer): Boolean;
|
|
procedure Paint; override;
|
|
procedure PickListItemSelected(Sender: TObject);
|
|
procedure PrepareCanvas(aCol,aRow: Integer; aState:TGridDrawState); virtual;
|
|
procedure PrepareCellHints(ACol, ARow: Integer); virtual;
|
|
procedure ResetDefaultColWidths; virtual;
|
|
procedure ResetEditor;
|
|
procedure ResetLastMove;
|
|
function ResetOffset(chkCol, ChkRow: Boolean): Boolean;
|
|
procedure ResetSizes; virtual;
|
|
procedure ResizeColumn(aCol, aWidth: Integer);
|
|
procedure ResizeRow(aRow, aHeight: Integer);
|
|
procedure RowHeightsChanged; virtual;
|
|
procedure SaveContent(cfg: TXMLConfig); virtual;
|
|
procedure SaveGridOptions(cfg: TXMLConfig); virtual;
|
|
procedure ScrollBarRange(Which:Integer; aRange,aPage,aPos: Integer);
|
|
procedure ScrollBarPosition(Which, Value: integer);
|
|
function ScrollBarIsVisible(Which:Integer): Boolean;
|
|
procedure ScrollBarPage(Which: Integer; aPage: Integer);
|
|
procedure ScrollBarShow(Which: Integer; aValue: boolean);
|
|
function ScrollBarAutomatic(Which: TScrollStyle): boolean; virtual;
|
|
procedure ScrollBy(DeltaX, DeltaY: Integer); override;
|
|
procedure SelectEditor; virtual;
|
|
function SelectCell(ACol, ARow: Integer): Boolean; virtual;
|
|
procedure SetCanvasFont(aFont: TFont);
|
|
procedure SetColCount(AValue: Integer); virtual;
|
|
procedure SetColor(Value: TColor); override;
|
|
procedure SetColRow(const ACol,ARow: Integer; withEvents: boolean = false);
|
|
procedure SetCursor(AValue: TCursor); override;
|
|
procedure SetEditText(ACol, ARow: Longint; const Value: string); virtual;
|
|
procedure SetBorderStyle(NewStyle: TBorderStyle); override;
|
|
procedure SetFixedcolor(const AValue: TColor); virtual;
|
|
procedure SetFixedCols(const AValue: Integer); virtual;
|
|
procedure SetFixedRows(const AValue: Integer); virtual;
|
|
procedure SetRawColWidths(ACol: Integer; AValue: Integer);
|
|
procedure SetSelectedColor(const AValue: TColor); virtual;
|
|
procedure SetFadeUnfocusedSelection(const AValue: boolean);
|
|
procedure ShowCellHintWindow(APoint: TPoint);
|
|
procedure SizeChanged(OldColCount, OldRowCount: Integer); virtual;
|
|
procedure Sort(ColSorting: Boolean; index,IndxFrom,IndxTo:Integer); virtual;
|
|
procedure StartPushCell;
|
|
procedure TopLeftChanged; virtual;
|
|
function TryMoveSelection(Relative: Boolean; var DCol, DRow: Integer): Boolean;
|
|
procedure UnLockEditor;
|
|
procedure UnprepareCellHints; virtual;
|
|
procedure UpdateHorzScrollBar(const aVisible: boolean; const aRange,aPage,aPos: Integer); virtual;
|
|
procedure UpdateSelectionRange;
|
|
procedure UpdateVertScrollbar(const aVisible: boolean; const aRange,aPage,aPos: Integer); virtual;
|
|
procedure UpdateBorderStyle;
|
|
function ValidateEntry(const ACol,ARow:Integer; const OldValue:string; var NewValue:string): boolean; virtual;
|
|
procedure VisualChange; virtual;
|
|
procedure WMSize(var Message: TLMSize); message LM_SIZE;
|
|
procedure WMHScroll(var message : TLMHScroll); message LM_HSCROLL;
|
|
procedure WMVScroll(var message : TLMVScroll); message LM_VSCROLL;
|
|
procedure WMKillFocus(var message: TLMKillFocus); message LM_KILLFOCUS;
|
|
procedure WMSetFocus(var message: TLMSetFocus); message LM_SETFOCUS;
|
|
procedure WndProc(var TheMessage : TLMessage); override;
|
|
|
|
property AllowOutboundEvents: boolean read FAllowOutboundEvents write FAllowOutboundEvents default true;
|
|
property AlternateColor: TColor read FAlternateColor write SetAlternateColor stored IsAltColorStored;
|
|
property AutoAdvance: TAutoAdvance read FAutoAdvance write FAutoAdvance default aaRight;
|
|
property AutoEdit: boolean read FAutoEdit write FAutoEdit default true;
|
|
property AutoFillColumns: boolean read FAutoFillColumns write SetAutoFillColumns default false;
|
|
property BorderStyle:TBorderStyle read FGridBorderStyle write SetBorderStyle default bsSingle;
|
|
property BorderColor: TColor read FBorderColor write SetBorderColor default cl3DDKShadow;
|
|
property CellHintPriority: TCellHintPriority read FCellHintPriority write FCellHintPriority default chpAllNoDefault;
|
|
property Col: Integer read FCol write SetCol;
|
|
property ColCount: Integer read GetColCount write SetColCount default 5;
|
|
property ColRow: TPoint read GetQuickColRow write SetQuickColRow;
|
|
property ColRowDraggingCursor: TCursor index gcsDragging read GetSpecialCursor write SetSpecialCursor default crMultiDrag;
|
|
property ColRowDragIndicatorColor: TColor read FColRowDragIndicatorColor write SetColRowDragIndicatorColor default clRed;
|
|
property ColSizingCursor: TCursor index gcsColWidthChanging read GetSpecialCursor write SetSpecialCursor default crHSplit;
|
|
property ColumnClickSorts: boolean read FColumnClickSorts write SetColumnClickSorts default false;
|
|
property Columns: TGridColumns read GetColumns write SetColumns stored IsColumnsStored;
|
|
property ColWidths[aCol: Integer]: Integer read GetColWidths write SetColWidths;
|
|
property DefaultColWidth: Integer read GetDefColWidth write SetDefColWidth stored DefaultColWidthIsStored;
|
|
property DefaultRowHeight: Integer read GetDefRowHeight write SetDefRowHeight stored DefaultRowHeightIsStored;
|
|
property DefaultDrawing: Boolean read FDefaultDrawing write SetDefaultDrawing default True;
|
|
property DefaultTextStyle: TTextStyle read FDefaultTextStyle write FDefaultTextStyle;
|
|
property DisabledFontColor: TColor read FDisabledFontColor write FDisabledFontColor default clGrayText;
|
|
property DragDx: Integer read FDragDx write FDragDx;
|
|
property Editor: TWinControl read FEditor write SetEditor;
|
|
property EditorBorderStyle: TBorderStyle read GetEditorBorderStyle write SetEditorBorderStyle;
|
|
property EditorMode: Boolean read FEditorMode write EditorSetMode;
|
|
property EditorKey: boolean read FEditorKey write FEditorKey;
|
|
property EditorOptions: Integer read FEditorOptions write SetEditorOptions;
|
|
property EditorShowing: boolean read FEditorShowing write FEditorShowing;
|
|
property ExtendedColSizing: boolean read FExtendedColSizing write FExtendedColSizing;
|
|
property ExtendedRowSizing: boolean read FExtendedRowSizing write FExtendedRowSizing;
|
|
property ExtendedSelect: boolean read FExtendedSelect write FExtendedSelect default true;
|
|
property FastEditing: boolean read FFastEditing write FFastEditing;
|
|
property AltColorStartNormal: boolean read FAltColorStartNormal write SetAltColorStartNormal;
|
|
property FixedCols: Integer read FFixedCols write SetFixedCols default 1;
|
|
property FixedRows: Integer read FFixedRows write SetFixedRows default 1;
|
|
property FixedColor: TColor read GetFixedColor write SetFixedcolor default clBtnFace;
|
|
property FixedGridLineColor: TColor read FFixedGridLineColor write SetFixedGridLineColor default cl3DDKShadow;
|
|
property FixedHotColor: TColor read FFixedHotColor write FFixedHotColor default cl3DLight;
|
|
property Flat: Boolean read FFlat write SetFlat default false;
|
|
property FocusColor: TColor read FFocusColor write SetFocusColor;
|
|
property FocusRectVisible: Boolean read FFocusRectVisible write SetFocusRectVisible;
|
|
property GCache: TGridDataCache read FGCAChe;
|
|
property GridFlags: TGridFlags read FGridFlags write FGridFlags;
|
|
property GridHeight: Integer read FGCache.GridHeight;
|
|
property GridLineColor: TColor read FGridLineColor write SetGridLineColor default clSilver;
|
|
property GridLineStyle: TPenStyle read FGridLineStyle write SetGridLineStyle default psSolid;
|
|
property GridLineWidth: Integer read FGridLineWidth write SetGridLineWidth default 1;
|
|
property GridWidth: Integer read FGCache.GridWidth;
|
|
property HeaderHotZones: TGridZoneSet read FHeaderHotZones write FHeaderHotZones default [gzFixedCols];
|
|
property HeaderPushZones: TGridZoneSet read FHeaderPushZones write FHeaderPushZones default [gzFixedCols];
|
|
property ImageIndexSortAsc: TImageIndex read FAscImgInd write FAscImgInd default -1;
|
|
property ImageIndexSortDesc: TImageIndex read FDescImgInd write FDescImgInd default -1;
|
|
property TabAdvance: TAutoAdvance read FTabAdvance write FTabAdvance default aaRightDown;
|
|
property TitleImageList: TImageList read FTitleImageList write SetTitleImageList;
|
|
property TitleImageListWidth: Integer read FTitleImageListWidth write SetTitleImageListWidth default 0;
|
|
property InplaceEditor: TWinControl read FEditor;
|
|
property IsCellSelected[aCol,aRow: Integer]: boolean read GetIsCellSelected;
|
|
property LeftCol:Integer read GetLeftCol write SetLeftCol;
|
|
property MouseWheelOption: TMouseWheelOption read FMouseWheelOption write FMouseWheelOption default mwCursor;
|
|
property Options: TGridOptions read FOptions write SetOptions default DefaultGridOptions;
|
|
property Options2: TGridOptions2 read FOptions2 write SetOptions2 default DefaultGridOptions2;
|
|
property RangeSelectMode: TRangeSelectMode read FRangeSelectMode write SetRangeSelectMode default rsmSingle;
|
|
property Row: Integer read FRow write SetRow;
|
|
property RowCount: Integer read GetRowCount write SetRowCount default 5;
|
|
property RowSizingCursor: TCursor index gcsRowHeightChanging read GetSpecialCursor write SetSpecialCursor default crVSplit;
|
|
property RowHeights[aRow: Integer]: Integer read GetRowHeights write SetRowHeights;
|
|
property SaveOptions: TSaveOptions read FsaveOptions write FSaveOptions;
|
|
property SelectActive: Boolean read FSelectActive write SetSelectActive;
|
|
property SelectedColor: TColor read GetSelectedColor write SetSelectedColor;
|
|
property FadeUnfocusedSelection: boolean read FFadeUnfocusedSelection write SetFadeUnfocusedSelection default false;
|
|
property SelectedColumn: TGridColumn read GetSelectedColumn;
|
|
property Selection: TGridRect read GetSelection write SetSelection;
|
|
property ScrollBars: TScrollStyle read FScrollBars write SetScrollBars default ssAutoBoth;
|
|
property StrictSort: boolean read FStrictSort write FStrictSort;
|
|
property TitleFont: TFont read FTitleFont write SetTitleFont stored TitleFontIsStored;
|
|
property TitleStyle: TTitleStyle read FTitleStyle write SetTitleStyle default tsLazarus;
|
|
property TopRow: Integer read GetTopRow write SetTopRow;
|
|
property UseXORFeatures: boolean read FUseXORFeatures write SetUseXorFeatures default false;
|
|
property ValidateOnSetSelection: boolean read FValidateOnSetSelection write FValidateOnSetSelection;
|
|
property VisibleColCount: Integer read GetVisibleColCount stored false;
|
|
property VisibleRowCount: Integer read GetVisibleRowCount stored false;
|
|
|
|
property OnAfterSelection: TOnSelectEvent read FOnAfterSelection write FOnAfterSelection;
|
|
property OnBeforeSelection: TOnSelectEvent read FOnBeforeSelection write FOnBeforeSelection;
|
|
property OnCheckboxToggled: TToggledCheckboxEvent read FOnCheckboxToggled write FOnCheckboxToggled;
|
|
property OnCompareCells: TOnCompareCells read FOnCompareCells write FOnCompareCells;
|
|
property OnPrepareCanvas: TOnPrepareCanvasEvent read FOnPrepareCanvas write FOnPrepareCanvas;
|
|
property OnDrawCell: TOnDrawCell read FOnDrawCell write FOnDrawCell;
|
|
// Deprecated in favor of OnButtonClick.
|
|
property OnEditButtonClick: TNotifyEvent read FOnEditButtonClick write FOnEditButtonClick; deprecated;
|
|
property OnButtonClick: TOnSelectEvent read FOnButtonClick write FOnButtonClick;
|
|
property OnPickListSelect: TNotifyEvent read FOnPickListSelect write FOnPickListSelect;
|
|
property OnSelection: TOnSelectEvent read fOnSelection write fOnSelection;
|
|
property OnSelectEditor: TSelectEditorEvent read FOnSelectEditor write FOnSelectEditor;
|
|
property OnTopLeftChanged: TNotifyEvent read FOnTopLeftChanged write FOnTopLeftChanged;
|
|
property OnUserCheckboxBitmap: TUserCheckboxBitmapEvent read FOnUserCheckboxBitmap write FOnUserCheckboxBitmap;
|
|
property OnUserCheckboxImage: TUserCheckBoxImageEvent read FOnUserCheckboxImage write FOnUserCheckboxImage;
|
|
property OnValidateEntry: TValidateEntryEvent read FOnValidateEntry write FOnValidateEntry;
|
|
// Bidi functions
|
|
function FlipRect(ARect: TRect): TRect;
|
|
function FlipPoint(P: TPoint): TPoint;
|
|
function FlipX(X: Integer): Integer;
|
|
// Hint-related
|
|
property OnGetCellHint : TGetCellHintEvent read FOnGetCellHint write FOnGetCellHint;
|
|
property OnSaveColumn: TSaveColumnEvent read FOnSaveColumn write FOnSaveColumn;
|
|
property OnLoadColumn: TSaveColumnEvent read FOnLoadColumn write FOnLoadColumn;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure Invalidate; override;
|
|
procedure EditingDone; override;
|
|
|
|
{ Exposed procs }
|
|
procedure AdjustInnerCellRect(var ARect: TRect);
|
|
procedure AutoAdjustColumns; virtual;
|
|
procedure BeginUpdate;
|
|
function CellRect(ACol, ARow: Integer): TRect;
|
|
function CellToGridZone(aCol,aRow: Integer): TGridZone;
|
|
procedure CheckPosition;
|
|
function ClearCols: Boolean;
|
|
function ClearRows: Boolean;
|
|
procedure Clear;
|
|
procedure ClearSelections;
|
|
|
|
function EditorByStyle(Style: TColumnButtonStyle): TWinControl; virtual;
|
|
procedure EditorKeyDown(Sender: TObject; var Key:Word; Shift:TShiftState);
|
|
procedure EditorKeyPress(Sender: TObject; var Key: Char);
|
|
procedure EditorUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
|
|
procedure EditorKeyUp(Sender: TObject; var key:Word; shift:TShiftState);
|
|
procedure EditorTextChanged(const aCol,aRow: Integer; const aText:string); virtual;
|
|
|
|
procedure EndUpdate(aRefresh: boolean = true);
|
|
procedure EraseBackground(DC: HDC); override;
|
|
procedure FixDesignFontsPPI(const ADesignTimePPI: Integer); override;
|
|
function Focused: Boolean; override;
|
|
function HasMultiSelection: Boolean;
|
|
procedure HideSortArrow;
|
|
procedure InvalidateCell(aCol, aRow: Integer); overload;
|
|
procedure InvalidateCol(ACol: Integer);
|
|
procedure InvalidateRange(const aRange: TRect);
|
|
procedure InvalidateRow(ARow: Integer);
|
|
function IsCellVisible(aCol, aRow: Integer): Boolean;
|
|
function IsFixedCellVisible(aCol, aRow: Integer): boolean;
|
|
procedure LoadFromFile(FileName: string); virtual;
|
|
procedure LoadFromStream(AStream: TStream); virtual;
|
|
function MouseCoord(X,Y: Integer): TGridCoord;
|
|
function MouseToCell(const Mouse: TPoint): TPoint; overload;
|
|
procedure MouseToCell(X,Y: Integer; out ACol,ARow: Longint); overload;
|
|
function MouseToLogcell(Mouse: TPoint): TPoint;
|
|
function MouseToGridZone(X,Y: Integer): TGridZone;
|
|
procedure SaveToFile(FileName: string); virtual;
|
|
procedure SaveToStream(AStream: TStream); virtual;
|
|
procedure ScaleFontsPPI(const AToPPI: Integer; const AProportion: Double); override;
|
|
procedure SetFocus; override;
|
|
|
|
property CursorState: TGridCursorState read FCursorState;
|
|
property SelectedRange[AIndex: Integer]: TGridRect read GetSelectedRange;
|
|
property SelectedRangeCount: Integer read GetSelectedRangeCount;
|
|
property SortOrder: TSortOrder read FSortOrder write FSortOrder;
|
|
property SortColumn: Integer read FSortColumn;
|
|
property TabStop default true;
|
|
{$ifdef WINDOWS}
|
|
protected
|
|
procedure IMEStartComposition(var Msg:TMessage); message WM_IME_STARTCOMPOSITION;
|
|
procedure IMEComposition(var Msg:TMessage); message WM_IME_COMPOSITION;
|
|
procedure IMEEndComposition(var Msg:TMessage); message WM_IME_ENDCOMPOSITION;
|
|
{$endif}
|
|
end;
|
|
|
|
TGetEditEvent = procedure (Sender: TObject; ACol, ARow: Integer; var Value: string) of object;
|
|
TSetEditEvent = procedure (Sender: TObject; ACol, ARow: Integer; const Value: string) of object;
|
|
TGetCheckboxStateEvent = procedure (Sender: TObject; ACol, ARow: Integer; var Value: TCheckboxState) of object;
|
|
TSetCheckboxStateEvent = procedure (Sender: TObject; ACol, ARow: Integer; const Value: TCheckboxState) of object;
|
|
|
|
{ TCustomDrawGrid }
|
|
|
|
TCustomDrawGrid=class(TCustomGrid)
|
|
private
|
|
FEditorRow, FEditorCol: Integer;
|
|
FOnColRowDeleted: TgridOperationEvent;
|
|
FOnColRowExchanged: TgridOperationEvent;
|
|
FOnColRowInserted: TGridOperationEvent;
|
|
FOnColRowMoved: TgridOperationEvent;
|
|
FOnGetCheckboxState: TGetCheckboxStateEvent;
|
|
FOnGetEditMask: TGetEditEvent;
|
|
FOnGetEditText: TGetEditEvent;
|
|
FOnHeaderClick, FOnHeaderSized: THdrEvent;
|
|
FOnHeaderSizing: THeaderSizingEvent;
|
|
FOnSelectCell: TOnSelectcellEvent;
|
|
FOnSetCheckboxState: TSetCheckboxStateEvent;
|
|
FOnSetEditText: TSetEditEvent;
|
|
function CellNeedsCheckboxBitmaps(const aCol,aRow: Integer): boolean;
|
|
procedure DrawCellCheckboxBitmaps(const aCol,aRow: Integer; const aRect: TRect);
|
|
function GetEditorValue(ACol, ARow: Integer): String;
|
|
protected
|
|
FGrid: TVirtualGrid;
|
|
procedure CellClick(const aCol,aRow: Integer; const Button:TMouseButton); override;
|
|
procedure ColRowDeleted(IsColumn: Boolean; index: Integer); override;
|
|
procedure ColRowExchanged(IsColumn: Boolean; index,WithIndex: Integer); override;
|
|
procedure ColRowInserted(IsColumn: boolean; index: integer); override;
|
|
procedure ColRowMoved(IsColumn: Boolean; FromIndex,ToIndex: Integer); override;
|
|
function CreateVirtualGrid: TVirtualGrid; virtual;
|
|
procedure DrawCell(aCol,aRow: Integer; aRect: TRect; aState:TGridDrawState); override;
|
|
procedure DrawCellAutonumbering(aCol,aRow: Integer; aRect: TRect; const aValue: string); virtual;
|
|
procedure DrawFocusRect(aCol,aRow: Integer; ARect: TRect); override;
|
|
function GetCells(ACol, ARow: Integer): string; override;
|
|
procedure GetCheckBoxState(const aCol, aRow:Integer; var aState:TCheckboxState); virtual;
|
|
function GetEditMask(aCol, aRow: Longint): string; override;
|
|
function GetEditText(aCol, aRow: Longint): string; override;
|
|
procedure GridMouseWheel(shift: TShiftState; Delta: Integer); override;
|
|
procedure HeaderClick(IsColumn: Boolean; index: Integer); override;
|
|
procedure HeaderSized(IsColumn: Boolean; index: Integer); override;
|
|
procedure HeaderSizing(const IsColumn:boolean; const AIndex,ASize:Integer); override;
|
|
procedure KeyDown(var Key : Word; Shift : TShiftState); override;
|
|
procedure NotifyColRowChange(WasInsert,IsColumn:boolean; FromIndex,ToIndex:Integer);
|
|
function SelectCell(aCol,aRow: Integer): boolean; override;
|
|
procedure SetColor(Value: TColor); override;
|
|
procedure SetCheckboxState(const aCol, aRow:Integer; const aState: TCheckboxState); virtual;
|
|
procedure SetEditText(ACol, ARow: Longint; const Value: string); override;
|
|
procedure SizeChanged(OldColCount, OldRowCount: Integer); override;
|
|
procedure ToggleCheckbox; virtual;
|
|
|
|
property OnGetCheckboxState: TGetCheckboxStateEvent
|
|
read FOnGetCheckboxState write FOnGetCheckboxState;
|
|
property OnSetCheckboxState: TSetCheckboxStateEvent
|
|
read FOnSetCheckboxState write FOnSetCheckboxState;
|
|
|
|
public
|
|
|
|
// to easy user call
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure DeleteColRow(IsColumn: Boolean; index: Integer);
|
|
procedure DeleteCol(Index: Integer); virtual;
|
|
procedure DeleteRow(Index: Integer); virtual;
|
|
procedure ExchangeColRow(IsColumn: Boolean; index, WithIndex: Integer); virtual;
|
|
procedure InsertColRow(IsColumn: boolean; index: integer);
|
|
procedure MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer);
|
|
procedure SortColRow(IsColumn: Boolean; index:Integer); overload;
|
|
procedure SortColRow(IsColumn: Boolean; Index,FromIndex,ToIndex: Integer); overload;
|
|
|
|
procedure DefaultDrawCell(aCol,aRow: Integer; var aRect: TRect; aState:TGridDrawState); virtual;
|
|
// properties
|
|
property AllowOutboundEvents;
|
|
property BorderColor;
|
|
property Canvas;
|
|
property Col;
|
|
property ColWidths;
|
|
property ColRow;
|
|
property DisabledFontColor;
|
|
property Editor;
|
|
property EditorBorderStyle;
|
|
property EditorMode;
|
|
property ExtendedColSizing;
|
|
property AltColorStartNormal;
|
|
property FastEditing;
|
|
property FixedGridLineColor;
|
|
property FocusColor;
|
|
property FocusRectVisible;
|
|
property GridHeight;
|
|
property GridWidth;
|
|
property IsCellSelected;
|
|
property LeftCol;
|
|
property Row;
|
|
property RowHeights;
|
|
property SaveOptions;
|
|
property SelectedColor;
|
|
property SelectedColumn;
|
|
property Selection;
|
|
property StrictSort;
|
|
//property TabStops;
|
|
property TopRow;
|
|
property UseXORFeatures;
|
|
public
|
|
property Align;
|
|
property Anchors;
|
|
property AutoAdvance;
|
|
property AutoFillColumns;
|
|
//property BiDiMode;
|
|
property BorderSpacing;
|
|
property BorderStyle;
|
|
property Color default clWindow;
|
|
property ColCount;
|
|
property Columns;
|
|
property Constraints;
|
|
property DefaultColWidth;
|
|
property DefaultDrawing;
|
|
property DefaultRowHeight;
|
|
//property DragCursor;
|
|
//property DragKind;
|
|
//property DragMode;
|
|
property Enabled;
|
|
property FadeUnfocusedSelection;
|
|
property FixedColor;
|
|
property FixedCols;
|
|
property FixedHotColor;
|
|
property FixedRows;
|
|
property Flat;
|
|
property Font;
|
|
property GridLineColor;
|
|
property GridLineStyle;
|
|
property GridLineWidth;
|
|
property Options;
|
|
property Options2;
|
|
//property ParentBiDiMode;
|
|
//property ParentColor;
|
|
//property ParentFont;
|
|
property ParentShowHint;
|
|
property PopupMenu;
|
|
property RowCount;
|
|
property ScrollBars;
|
|
property ShowHint;
|
|
property TabAdvance;
|
|
property TabOrder;
|
|
property TabStop;
|
|
property Visible;
|
|
property VisibleColCount;
|
|
property VisibleRowCount;
|
|
|
|
property OnAfterSelection;
|
|
property OnBeforeSelection;
|
|
property OnClick;
|
|
property OnColRowDeleted: TgridOperationEvent read FOnColRowDeleted write FOnColRowDeleted;
|
|
property OnColRowExchanged: TgridOperationEvent read FOnColRowExchanged write FOnColRowExchanged;
|
|
property OnColRowInserted: TGridOperationEvent read FOnColRowInserted write FOnColRowInserted;
|
|
property OnColRowMoved: TgridOperationEvent read FOnColRowMoved write FOnColRowMoved;
|
|
property OnCompareCells;
|
|
property OnContextPopup;
|
|
property OnDblClick;
|
|
property OnDragDrop;
|
|
property OnDragOver;
|
|
property OnDrawCell;
|
|
property OnEditButtonClick; deprecated;
|
|
property OnButtonClick;
|
|
property OnEndDock;
|
|
property OnEndDrag;
|
|
property OnEnter;
|
|
property OnExit;
|
|
property OnGetEditMask: TGetEditEvent read FOnGetEditMask write FOnGetEditMask;
|
|
property OnGetEditText: TGetEditEvent read FOnGetEditText write FOnGetEditText;
|
|
property OnHeaderClick: THdrEvent read FOnHeaderClick write FOnHeaderClick;
|
|
property OnHeaderSized: THdrEvent read FOnHeaderSized write FOnHeaderSized;
|
|
property OnHeaderSizing: THeaderSizingEvent read FOnHeaderSizing write FOnHeaderSizing;
|
|
property OnKeyDown;
|
|
property OnKeyPress;
|
|
property OnKeyUp;
|
|
property OnMouseDown;
|
|
property OnMouseEnter;
|
|
property OnMouseLeave;
|
|
property OnMouseMove;
|
|
property OnMouseUp;
|
|
property OnMouseWheel;
|
|
property OnMouseWheelDown;
|
|
property OnMouseWheelUp;
|
|
property OnPickListSelect;
|
|
property OnPrepareCanvas;
|
|
property OnSelectEditor;
|
|
property OnSelection;
|
|
property OnSelectCell: TOnSelectCellEvent read FOnSelectCell write FOnSelectCell;
|
|
property OnSetEditText: TSetEditEvent read FOnSetEditText write FOnSetEditText;
|
|
property OnStartDock;
|
|
property OnStartDrag;
|
|
property OnTopleftChanged;
|
|
property OnUTF8KeyPress;
|
|
property OnValidateEntry;
|
|
end;
|
|
|
|
|
|
|
|
{ TDrawGrid }
|
|
|
|
TDrawGrid = class(TCustomDrawGrid)
|
|
public
|
|
property InplaceEditor;
|
|
published
|
|
property Align;
|
|
property AlternateColor;
|
|
property Anchors;
|
|
property AutoAdvance;
|
|
property AutoEdit;
|
|
property AutoFillColumns;
|
|
//property BiDiMode;
|
|
property BorderSpacing;
|
|
property BorderStyle;
|
|
// property CellHintPriority;
|
|
property Color;
|
|
property ColCount;
|
|
property ColRowDraggingCursor;
|
|
property ColRowDragIndicatorColor;
|
|
property ColSizingCursor;
|
|
property ColumnClickSorts;
|
|
property Columns;
|
|
property Constraints;
|
|
property DefaultColWidth;
|
|
property DefaultDrawing;
|
|
property DefaultRowHeight;
|
|
property DoubleBuffered;
|
|
property DragCursor;
|
|
property DragKind;
|
|
property DragMode;
|
|
property Enabled;
|
|
property ExtendedSelect;
|
|
property FadeUnfocusedSelection;
|
|
property FixedColor;
|
|
property FixedCols;
|
|
property FixedRows;
|
|
property Flat;
|
|
property Font;
|
|
property GridLineColor;
|
|
property GridLineStyle;
|
|
property GridLineWidth;
|
|
property HeaderHotZones;
|
|
property HeaderPushZones;
|
|
property ImageIndexSortAsc;
|
|
property ImageIndexSortDesc;
|
|
property MouseWheelOption;
|
|
property Options;
|
|
property Options2;
|
|
//property ParentBiDiMode;
|
|
property ParentColor default false;
|
|
property ParentDoubleBuffered;
|
|
property ParentFont;
|
|
property ParentShowHint;
|
|
property PopupMenu;
|
|
property RangeSelectMode;
|
|
property RowCount;
|
|
property RowSizingCursor;
|
|
property ScrollBars;
|
|
property ShowHint;
|
|
property TabAdvance;
|
|
property TabOrder;
|
|
property TabStop;
|
|
property TitleFont;
|
|
property TitleImageList;
|
|
property TitleImageListWidth;
|
|
property TitleStyle;
|
|
property UseXORFeatures;
|
|
property Visible;
|
|
property VisibleColCount;
|
|
property VisibleRowCount;
|
|
|
|
property OnAfterSelection;
|
|
property OnBeforeSelection;
|
|
property OnCheckboxToggled;
|
|
property OnClick;
|
|
property OnColRowDeleted;
|
|
property OnColRowExchanged;
|
|
property OnColRowInserted;
|
|
property OnColRowMoved;
|
|
property OnCompareCells;
|
|
property OnContextPopup;
|
|
property OnDblClick;
|
|
property OnDragDrop;
|
|
property OnDragOver;
|
|
property OnDrawCell;
|
|
property OnEditButtonClick; deprecated;
|
|
property OnButtonClick;
|
|
property OnEditingDone;
|
|
property OnEndDock;
|
|
property OnEndDrag;
|
|
property OnEnter;
|
|
property OnExit;
|
|
property OnGetCellHint;
|
|
property OnGetCheckboxState;
|
|
property OnGetEditMask;
|
|
property OnGetEditText;
|
|
property OnHeaderClick;
|
|
property OnHeaderSized;
|
|
property OnHeaderSizing;
|
|
property OnKeyDown;
|
|
property OnKeyPress;
|
|
property OnKeyUp;
|
|
property OnMouseDown;
|
|
property OnMouseEnter;
|
|
property OnMouseLeave;
|
|
property OnMouseMove;
|
|
property OnMouseUp;
|
|
property OnMouseWheel;
|
|
property OnMouseWheelDown;
|
|
property OnMouseWheelUp;
|
|
property OnMouseWheelHorz;
|
|
property OnMouseWheelLeft;
|
|
property OnMouseWheelRight;
|
|
property OnPickListSelect;
|
|
property OnPrepareCanvas;
|
|
property OnSelectEditor;
|
|
property OnSelection;
|
|
property OnSelectCell;
|
|
property OnSetCheckboxState;
|
|
property OnSetEditText;
|
|
property OnStartDock;
|
|
property OnStartDrag;
|
|
property OnTopleftChanged;
|
|
property OnUserCheckboxBitmap;
|
|
property OnUserCheckboxImage;
|
|
property OnUTF8KeyPress;
|
|
property OnValidateEntry;
|
|
end;
|
|
|
|
TCustomStringGrid = class;
|
|
|
|
{ TStringGridStrings }
|
|
|
|
TStringGridStrings = class(TStrings)
|
|
private
|
|
FAddedCount: Integer;
|
|
FGrid: TCustomStringGrid;
|
|
FIsCol: Boolean;
|
|
FIndex: Integer;
|
|
FOwner: TMap;
|
|
function ConvertIndexLineCol(Index: Integer; var Line, Col: Integer): boolean;
|
|
protected
|
|
function Get(Index: Integer): string; override;
|
|
function GetCount: Integer; override;
|
|
function GetObject(Index: Integer): TObject; override;
|
|
procedure Put(Index: Integer; const S: string); override;
|
|
procedure PutObject(Index: Integer; aObject: TObject); override;
|
|
public
|
|
constructor Create(aGrid: TCustomStringGrid; OwnerMap:TMap; aIsCol: Boolean; aIndex: Longint);
|
|
destructor Destroy; override;
|
|
function Add(const S: string): Integer; override;
|
|
procedure Assign(Source: TPersistent); override;
|
|
procedure Clear; override;
|
|
procedure Delete(Index: Integer); override;
|
|
procedure Insert(Index: Integer; const S: string); override;
|
|
end;
|
|
|
|
|
|
{ TCustomStringGrid }
|
|
|
|
TCustomStringGrid = class(TCustomDrawGrid)
|
|
private
|
|
FModified: boolean;
|
|
FColsMap,FRowsMap: TMap;
|
|
fOnCellProcess: TCellProcessEvent;
|
|
function GetCols(index: Integer): TStrings;
|
|
function GetObjects(ACol, ARow: Integer): TObject;
|
|
function GetRows(index: Integer): TStrings;
|
|
procedure MapFree(var aMap: TMap);
|
|
function MapGetColsRows(IsCols: boolean; Index:Integer; var AMap:TMap):TStrings;
|
|
procedure ReadCells(Reader: TReader);
|
|
procedure SetCols(index: Integer; const AValue: TStrings);
|
|
procedure SetObjects(ACol, ARow: Integer; AValue: TObject);
|
|
procedure SetRows(index: Integer; const AValue: TStrings);
|
|
procedure WriteCells(Writer: TWriter);
|
|
procedure CopyCellRectToClipboard(const R:TRect);
|
|
protected
|
|
procedure AssignTo(Dest: TPersistent); override;
|
|
procedure AutoAdjustColumn(aCol: Integer); override;
|
|
procedure CalcCellExtent(acol, aRow: Integer; var aRect: TRect); override;
|
|
procedure DefineProperties(Filer: TFiler); override;
|
|
procedure DefineCellsProperty(Filer: TFiler); virtual;
|
|
function DoCompareCells(Acol,ARow,Bcol,BRow: Integer): Integer; override;
|
|
procedure DoCopyToClipboard; override;
|
|
procedure DoCutToClipboard; override;
|
|
procedure DoPasteFromClipboard; override;
|
|
procedure DoCellProcess(aCol, aRow: Integer; processType: TCellProcessType; var aValue: string); virtual;
|
|
procedure DrawColumnText(aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState); override;
|
|
procedure DrawTextInCell(aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState); override;
|
|
procedure DrawCellAutonumbering(aCol,aRow: Integer; aRect: TRect; const aValue: string); override;
|
|
//procedure EditordoGetValue; override;
|
|
//procedure EditordoSetValue; override;
|
|
function GetCells(ACol, ARow: Integer): string; override;
|
|
procedure GetCheckBoxState(const aCol, aRow:Integer; var aState:TCheckboxState); override;
|
|
function GetEditText(aCol, aRow: Integer): string; override;
|
|
procedure LoadContent(cfg: TXMLConfig; Version: Integer); override;
|
|
procedure Loaded; override;
|
|
procedure SaveContent(cfg: TXMLConfig); override;
|
|
//procedure DrawInteriorCells; override;
|
|
//procedure SelectEditor; override;
|
|
procedure SelectionSetText(TheText: String);
|
|
procedure SelectionSetHTML(TheHTML, TheText: String);
|
|
procedure SetCells(ACol, ARow: Integer; const AValue: string); virtual;
|
|
procedure SetCheckboxState(const aCol, aRow:Integer; const aState: TCheckboxState); override;
|
|
procedure SetEditText(aCol, aRow: Longint; const aValue: string); override;
|
|
|
|
property Modified: boolean read FModified write FModified;
|
|
property OnCellProcess: TCellProcessEvent read fOnCellProcess write fOnCellProcess;
|
|
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure AutoSizeColumn(aCol: Integer);
|
|
procedure AutoSizeColumns;
|
|
procedure Clean; overload;
|
|
procedure Clean(CleanOptions: TGridZoneSet); overload;
|
|
procedure Clean(aRect: TRect; CleanOptions: TGridZoneSet); overload;
|
|
procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions: TGridZoneSet); overload;
|
|
procedure CopyToClipboard(AUseSelection: boolean = false);
|
|
procedure InsertRowWithValues(Index: Integer; Values: array of String);
|
|
procedure LoadFromCSVStream(AStream: TStream; ADelimiter: Char=',';
|
|
UseTitles: boolean=true; FromLine: Integer=0; SkipEmptyLines: Boolean=true); virtual;
|
|
procedure LoadFromCSVFile(AFilename: string; ADelimiter: Char=',';
|
|
UseTitles: boolean=true; FromLine: Integer=0; SkipEmptyLines: Boolean=true);
|
|
procedure SaveToCSVStream(AStream: TStream; ADelimiter: Char=',';
|
|
WriteTitles: boolean=true; VisibleColumnsOnly: boolean=false);
|
|
procedure SaveToCSVFile(AFileName: string; ADelimiter: Char=',';
|
|
WriteTitles: boolean=true; VisibleColumnsOnly: boolean=false);
|
|
|
|
property Cells[ACol, ARow: Integer]: string read GetCells write SetCells;
|
|
property Cols[index: Integer]: TStrings read GetCols write SetCols;
|
|
property DefaultTextStyle;
|
|
property EditorMode;
|
|
property ExtendedSelect;
|
|
property Objects[ACol, ARow: Integer]: TObject read GetObjects write SetObjects;
|
|
property Rows[index: Integer]: TStrings read GetRows write SetRows;
|
|
property UseXORFeatures;
|
|
property ValidateOnSetSelection;
|
|
end;
|
|
|
|
|
|
{ TStringGrid }
|
|
|
|
TStringGrid = class(TCustomStringGrid)
|
|
protected
|
|
class procedure WSRegisterClass; override;
|
|
public
|
|
property Modified;
|
|
property InplaceEditor;
|
|
published
|
|
property Align;
|
|
property AlternateColor;
|
|
property Anchors;
|
|
property AutoAdvance;
|
|
property AutoEdit;
|
|
property AutoFillColumns;
|
|
property BiDiMode;
|
|
property BorderSpacing;
|
|
property BorderStyle;
|
|
property CellHintPriority;
|
|
property Color;
|
|
property ColCount;
|
|
property ColRowDraggingCursor;
|
|
property ColRowDragIndicatorColor;
|
|
property ColSizingCursor;
|
|
property ColumnClickSorts;
|
|
property Columns;
|
|
property Constraints;
|
|
property DefaultColWidth;
|
|
property DefaultDrawing;
|
|
property DefaultRowHeight;
|
|
property DoubleBuffered;
|
|
property DragCursor;
|
|
property DragKind;
|
|
property DragMode;
|
|
property Enabled;
|
|
property ExtendedSelect;
|
|
property FadeUnfocusedSelection;
|
|
property FixedColor;
|
|
property FixedCols;
|
|
property FixedRows;
|
|
property Flat;
|
|
property Font;
|
|
property GridLineColor;
|
|
property GridLineStyle;
|
|
property GridLineWidth;
|
|
property HeaderHotZones;
|
|
property HeaderPushZones;
|
|
property ImageIndexSortAsc;
|
|
property ImageIndexSortDesc;
|
|
property MouseWheelOption;
|
|
property Options;
|
|
property Options2;
|
|
property ParentBiDiMode;
|
|
property ParentColor default false;
|
|
property ParentDoubleBuffered;
|
|
property ParentFont;
|
|
property ParentShowHint;
|
|
property PopupMenu;
|
|
property RangeSelectMode;
|
|
property RowCount;
|
|
property RowSizingCursor;
|
|
property ScrollBars;
|
|
property ShowHint;
|
|
property TabAdvance;
|
|
property TabOrder;
|
|
property TabStop;
|
|
property TitleFont;
|
|
property TitleImageList;
|
|
property TitleStyle;
|
|
property UseXORFeatures;
|
|
property Visible;
|
|
property VisibleColCount;
|
|
property VisibleRowCount;
|
|
|
|
property OnAfterSelection;
|
|
property OnBeforeSelection;
|
|
property OnCellProcess;
|
|
property OnChangeBounds;
|
|
property OnCheckboxToggled;
|
|
property OnClick;
|
|
property OnColRowDeleted;
|
|
property OnColRowExchanged;
|
|
property OnColRowInserted;
|
|
property OnColRowMoved;
|
|
property OnCompareCells;
|
|
property OnContextPopup;
|
|
property OnDragDrop;
|
|
property OnDragOver;
|
|
property OnDblClick;
|
|
property OnDrawCell;
|
|
property OnEditButtonClick; deprecated;
|
|
property OnButtonClick;
|
|
property OnEditingDone;
|
|
property OnEndDock;
|
|
property OnEndDrag;
|
|
property OnEnter;
|
|
property OnExit;
|
|
property OnGetCellHint;
|
|
property OnGetCheckboxState;
|
|
property OnGetEditMask;
|
|
property OnGetEditText;
|
|
property OnHeaderClick;
|
|
property OnHeaderSized;
|
|
property OnHeaderSizing;
|
|
property OnKeyDown;
|
|
property OnKeyPress;
|
|
property OnKeyUp;
|
|
property OnMouseDown;
|
|
property OnMouseEnter;
|
|
property OnMouseLeave;
|
|
property OnMouseMove;
|
|
property OnMouseUp;
|
|
property OnMouseWheel;
|
|
property OnMouseWheelDown;
|
|
property OnMouseWheelUp;
|
|
property OnMouseWheelHorz;
|
|
property OnMouseWheelLeft;
|
|
property OnMouseWheelRight;
|
|
property OnPickListSelect;
|
|
property OnPrepareCanvas;
|
|
property OnResize;
|
|
property OnSelectEditor;
|
|
property OnSelection;
|
|
property OnSelectCell;
|
|
property OnSetCheckboxState;
|
|
property OnSetEditText;
|
|
property OnShowHint;
|
|
property OnStartDock;
|
|
property OnStartDrag;
|
|
property OnTopLeftChanged;
|
|
property OnUserCheckboxBitmap;
|
|
property OnUserCheckboxImage;
|
|
property OnUTF8KeyPress;
|
|
property OnValidateEntry;
|
|
end;
|
|
|
|
procedure DrawRubberRect(Canvas: TCanvas; aRect: TRect; Color: TColor; DrawBits:Byte=BF_RECT);
|
|
function GetWorkingCanvas(const Canvas: TCanvas): TCanvas;
|
|
procedure FreeWorkingCanvas(canvas: TCanvas);
|
|
|
|
procedure Register;
|
|
|
|
implementation
|
|
|
|
{$R lcl_grid_images.res}
|
|
|
|
uses
|
|
WSGrids, GraphMath;
|
|
|
|
{$WARN SYMBOL_DEPRECATED OFF}
|
|
{$IFDEF FPC_HAS_CPSTRING}
|
|
{$WARN IMPLICIT_STRING_CAST OFF}
|
|
{$WARN IMPLICIT_STRING_CAST_LOSS OFF}
|
|
{$ENDIF}
|
|
|
|
type
|
|
TWinControlAccess = Class(TWinControl); //used in TCustomGrid.DoEditorShow
|
|
|
|
const
|
|
MULTISEL_MODIFIER = {$IFDEF Darwin}ssMeta{$ELSE}ssCtrl{$ENDIF};
|
|
|
|
function BidiFlipX(X: Integer; const Width: Integer; const Flip: Boolean): Integer;
|
|
begin
|
|
if Flip then
|
|
//-1 because it zero based
|
|
Result := Width - X - 1
|
|
else
|
|
Result := X;
|
|
end;
|
|
|
|
function BidiFlipX(X: Integer; const ParentRect: TRect; const Flip: Boolean): Integer;
|
|
begin
|
|
Result := BidiFlipX(X, ParentRect.Right, Flip);
|
|
end;
|
|
|
|
function BidiFlipPoint(P: TPoint; const ParentRect: TRect; const Flip: Boolean): TPoint;
|
|
begin
|
|
Result := P;
|
|
Result.Y := BidiFlipX(Result.Y, ParentRect, Flip);
|
|
end;
|
|
|
|
function NormalizarRect(const R:TRect): TRect;
|
|
begin
|
|
Result.Left:=Min(R.Left, R.Right);
|
|
Result.Top:=Min(R.Top, R.Bottom);
|
|
Result.Right:=Max(R.Left, R.Right);
|
|
Result.Bottom:=Max(R.Top, R.Bottom);
|
|
end;
|
|
|
|
procedure SwapInt(var I1,I2: Integer);
|
|
var
|
|
Tmp: Integer;
|
|
begin
|
|
Tmp:=I1;
|
|
I1:=I2;
|
|
I2:=Tmp;
|
|
end;
|
|
|
|
{$ifdef GridTraceMsg}
|
|
function TransMsg(const S: String; const TheMsg: TLMessage): String;
|
|
begin
|
|
case TheMsg.Msg of
|
|
CM_BASE..CM_MOUSEWHEEL:
|
|
case TheMsg.Msg of
|
|
CM_MOUSEENTER: exit; //Result := 'CM_MOUSEENTER';
|
|
CM_MOUSELEAVE: exit; //Result := 'CM_MOUSELEAVE';
|
|
CM_TEXTCHANGED: Result := 'CM_TEXTCHANGED';
|
|
CM_UIACTIVATE: Result := 'CM_UIACTIVATE';
|
|
CM_CONTROLLISTCHANGE: Result := 'CM_CONTROLLISTCHANGE';
|
|
|
|
CM_PARENTCOLORCHANGED: Result := 'CM_PARENTCOLORCHANGED';
|
|
CM_PARENTSHOWHINTCHANGED: Result := 'CM_PARENTSHOWHINTCHANGED';
|
|
CM_PARENTBIDIMODECHANGED: Result := 'CM_PARENTBIDIMODECHANGED';
|
|
CM_CONTROLCHANGE: Result := 'CM_CONTROLCHANGE';
|
|
CM_SHOWINGCHANGED: Result := 'CM_SHOWINGCHANGED';
|
|
CM_VISIBLECHANGED: Result := 'CM_VISIBLECHANGED';
|
|
CM_HITTEST: exit;//Result := 'CM_HITTEST';
|
|
else Result := 'CM_BASE + '+ IntToStr(TheMsg.Msg - CM_BASE);
|
|
end;
|
|
else
|
|
case TheMsg.Msg of
|
|
//CN_BASE MESSAGES
|
|
CN_COMMAND: Result := 'CN_COMMAND';
|
|
CN_KEYDOWN: Result := 'CN_KEYDOWN';
|
|
CN_KEYUP: Result := 'CN_KEYUP';
|
|
CN_CHAR: Result := 'CN_CHAR';
|
|
|
|
// NORMAL MESSAGES
|
|
LM_SETFOCUS: Result := 'LM_SetFocus';
|
|
LM_LBUTTONDOWN: Result := 'LM_MOUSEDOWN';
|
|
LM_LBUTTONUP: Result := 'LM_LBUTTONUP';
|
|
LM_LBUTTONDBLCLK: Result := 'LM_LBUTTONDBLCLK';
|
|
LM_RBUTTONDOWN: Result := 'LM_RBUTTONDOWN';
|
|
LM_RBUTTONUP: Result := 'LM_RBUTTONUP';
|
|
LM_RBUTTONDBLCLK: Result := 'LM_RBUTTONDBLCLK';
|
|
LM_GETDLGCODE: Result := 'LM_GETDLGCODE';
|
|
LM_KEYDOWN: Result := 'LM_KEYDOWN';
|
|
LM_KEYUP: Result := 'LM_KEYUP';
|
|
LM_CAPTURECHANGED: Result := 'LM_CAPTURECHANGED';
|
|
LM_ERASEBKGND: Result := 'LM_ERASEBKGND';
|
|
LM_KILLFOCUS: Result := 'LM_KILLFOCUS';
|
|
LM_CHAR: Result := 'LM_CHAR';
|
|
LM_SHOWWINDOW: Result := 'LM_SHOWWINDOW';
|
|
LM_SIZE: Result := 'LM_SIZE';
|
|
LM_WINDOWPOSCHANGED: Result := 'LM_WINDOWPOSCHANGED';
|
|
LM_HSCROLL: Result := 'LM_HSCROLL';
|
|
LM_VSCROLL: Result := 'LM_VSCROLL';
|
|
LM_MOUSEMOVE: exit;//Result := 'LM_MOUSEMOVE';
|
|
LM_MOUSEWHEEL: Result := 'LM_MOUSEWHEEL';
|
|
1105: exit;//Result := '?EM_SETWORDBREAKPROCEX?';
|
|
else Result := GetMessageName(TheMsg.Msg);
|
|
end;
|
|
end;
|
|
Result:= S + '['+IntToHex(TheMsg.msg, 8)+'] W='+IntToHex(TheMsg.WParam,8)+
|
|
' L='+IntToHex(TheMsg.LParam,8)+' '+Result;
|
|
DebugLn(Result);
|
|
end;
|
|
{$Endif GridTraceMsg}
|
|
|
|
function dbgs(zone: TGridZone):string; overload;
|
|
begin
|
|
case Zone of
|
|
gzFixedCells: Result := 'gzFixedCells';
|
|
gzFixedCols: Result := 'gzFixedCols';
|
|
gzFixedRows: Result := 'gzFixedRows';
|
|
gzNormal: Result := 'gzNormal';
|
|
gzInvalid: Result := 'gzInvalid';
|
|
else
|
|
result:= 'gz-error';
|
|
end;
|
|
end;
|
|
|
|
function dbgs(zones: TGridZoneSet):string; overload;
|
|
procedure add(const s:string);
|
|
begin
|
|
if result<>'' then
|
|
result := result + ',' + s
|
|
else
|
|
result := s;
|
|
end;
|
|
begin
|
|
result:='';
|
|
if gzFixedCells in zones then add('gzFixedCells');
|
|
if gzFixedCols in zones then add('gzFixedCols');
|
|
if gzFixedRows in zones then add('gzFixedRows');
|
|
if gzNormal in zones then add('gzNormal');
|
|
if gzInvalid in zones then add('gzInvalid');
|
|
result := '['+result+']';
|
|
end;
|
|
|
|
{$ifdef DbgScroll}
|
|
function SbToStr(Which: Integer): string;
|
|
begin
|
|
case Which of
|
|
SB_VERT: result := 'vert';
|
|
SB_HORZ: result := 'horz';
|
|
SB_BOTH: result := 'both';
|
|
else
|
|
result := '????';
|
|
end;
|
|
end;
|
|
{$endif}
|
|
|
|
procedure CfgSetFontValue(cfg: TXMLConfig; AKey: WideString; AFont: TFont);
|
|
begin
|
|
cfg.SetValue(AKey + '/name/value', AFont.Name);
|
|
cfg.SetValue(AKey + '/size/value', AFont.Size);
|
|
cfg.SetValue(AKey + '/color/value', ColorToString(AFont.Color));
|
|
cfg.SetValue(AKey + '/style/value', Integer(AFont.Style));
|
|
end;
|
|
|
|
procedure CfgGetFontValue(cfg: TXMLConfig; AKey: WideString; AFont: TFont);
|
|
begin
|
|
AFont.Name := cfg.GetValue(AKey + '/name/value', 'default');
|
|
AFont.Size := cfg.GetValue(AKey + '/size/value', 0);
|
|
AFont.Color:= StringToColor(cfg.GetValue(AKey + '/color/value', 'clWindowText'));
|
|
AFont.Style:= TFontStyles(cfg.GetValue(AKey + '/style/value', 0));
|
|
end;
|
|
|
|
// Draws a dotted rectangle by drawing each enabled side. By default all sides are
|
|
// enabled. The DrawBits parameter set sides to drawn, it has this layout: xxxxBRTL
|
|
procedure DrawRubberRect(Canvas: TCanvas; aRect: TRect; Color: TColor;
|
|
DrawBits: Byte);
|
|
procedure DrawVertLine(X1,Y1,Y2: integer);
|
|
begin
|
|
if Y2<Y1 then
|
|
while Y2<Y1 do begin
|
|
Canvas.Pixels[X1, Y1] := Color;
|
|
dec(Y1, varRubberSpace);
|
|
end
|
|
else
|
|
while Y1<Y2 do begin
|
|
Canvas.Pixels[X1, Y1] := Color;
|
|
inc(Y1, varRubberSpace);
|
|
end;
|
|
end;
|
|
procedure DrawHorzLine(X1,Y1,X2: integer);
|
|
begin
|
|
if X2<X1 then
|
|
while X2<X1 do begin
|
|
Canvas.Pixels[X1, Y1] := Color;
|
|
dec(X1, varRubberSpace);
|
|
end
|
|
else
|
|
while X1<X2 do begin
|
|
Canvas.Pixels[X1, Y1] := Color;
|
|
inc(X1, varRubberSpace);
|
|
end;
|
|
end;
|
|
begin
|
|
with aRect do begin
|
|
if (DrawBits and BF_TOP = BF_TOP) then DrawHorzLine(Left, Top, Right-1);
|
|
if (DrawBits and BF_RIGHT = BF_RIGHT) then DrawVertLine(Right-1, Top, Bottom-1);
|
|
if (DrawBits and BF_BOTTOM = BF_BOTTOM) then DrawHorzLine(Right-1, Bottom-1, Left);
|
|
if (DrawBits and BF_LEFT = BF_LEFT) then DrawVertLine(Left, Bottom-1, Top);
|
|
end;
|
|
end;
|
|
|
|
function GetWorkingCanvas(const Canvas: TCanvas): TCanvas;
|
|
var
|
|
DC: HDC;
|
|
begin
|
|
|
|
if (Canvas=nil) or (not Canvas.HandleAllocated) then begin
|
|
DC := GetDC(0);
|
|
Result := TCanvas.Create;
|
|
Result.Handle := DC;
|
|
end else
|
|
Result := Canvas;
|
|
|
|
end;
|
|
|
|
procedure FreeWorkingCanvas(canvas: TCanvas);
|
|
begin
|
|
|
|
ReleaseDC(0, Canvas.Handle);
|
|
Canvas.Free;
|
|
|
|
end;
|
|
|
|
function Between(const AValue,AMin,AMax: Integer): boolean;
|
|
begin
|
|
if AMin<AMax then
|
|
result := InRange(AValue, AMin, AMax)
|
|
else
|
|
result := InRange(AValue, AMax, AMin);
|
|
end;
|
|
|
|
{ TGridScroller }
|
|
|
|
constructor TGridScroller.Create(DoScroll: TGridScrollerDoScroll);
|
|
begin
|
|
Callback := DoScroll;
|
|
Timer := TTimer.Create(nil);
|
|
Timer.OnTimer := @TimerTick;
|
|
Timer.Interval := 200;
|
|
end;
|
|
|
|
destructor TGridScroller.Destroy;
|
|
begin
|
|
FreeAndNil(Timer);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TGridScroller.TimerTick(Sender: TObject);
|
|
begin
|
|
if Assigned(Callback) then
|
|
Callback(Dir);
|
|
end;
|
|
|
|
procedure TGridScroller.Start(ADir: TPoint);
|
|
begin
|
|
Dir := ADir;
|
|
Timer.Enabled := True;
|
|
end;
|
|
|
|
{ TCustomGrid }
|
|
|
|
function TCustomGrid.GetRowHeights(Arow: Integer): Integer;
|
|
begin
|
|
if IsRowIndexValid(aRow) then
|
|
Result:=FRows[aRow]
|
|
else
|
|
Result:=-1;
|
|
if Result<0 then
|
|
Result:=DefaultRowHeight;
|
|
end;
|
|
|
|
function TCustomGrid.GetTopRow: Longint;
|
|
begin
|
|
Result:=fTopLeft.y;
|
|
end;
|
|
|
|
function TCustomGrid.GetVisibleColCount: Integer;
|
|
begin
|
|
with FGCache do begin
|
|
Result := VisibleGrid.Right-VisibleGrid.Left;
|
|
if GridWidth<=ClientWidth then
|
|
inc(Result)
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.GetVisibleRowCount: Integer;
|
|
begin
|
|
with FGCache do begin
|
|
Result:=VisibleGrid.bottom-VisibleGrid.top;
|
|
if GridHeight<=ClientHeight then
|
|
inc(Result);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.HeadersMouseMove(const X, Y: Integer);
|
|
var
|
|
P: TPoint;
|
|
Gz: TGridZone;
|
|
ButtonColumn: boolean;
|
|
begin
|
|
|
|
with FGCache do begin
|
|
|
|
Gz := MouseToGridZone(X,Y);
|
|
ButtonColumn := IsMouseOverCellButton(X, Y);
|
|
P := MouseToCell(Point(X, Y));
|
|
|
|
if (gz<>HotGridZone) or (P.x<>HotCell.x) or (P.y<>HotCell.y) then begin
|
|
ResetHotCell;
|
|
if (P.x>=0) and (P.y>=0) then begin
|
|
if ButtonColumn or (goHeaderHotTracking in Options) then begin
|
|
InvalidateCell(P.X, P.Y);
|
|
HotCell := P;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if ButtonColumn or (goHeaderPushedLook in Options) then begin
|
|
if ClickCellPushed then begin
|
|
if (P.X<>PushedCell.x) or (P.Y<>PushedCell.Y) then
|
|
ResetPushedCell(False);
|
|
end else
|
|
if IsPushCellActive() then begin
|
|
if (P.X=PushedCell.X) and (P.Y=PushedCell.Y) then begin
|
|
ClickCellPushed:=True;
|
|
InvalidateCell(P.X, P.Y);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
HotGridZone := Gz;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.InternalAutoFillColumns;
|
|
var
|
|
i, availableSize, avgSize, rest: Integer;
|
|
widths: array of record
|
|
aIndex, aMin, aMax, aPriority, aWidth: Integer;
|
|
end;
|
|
done, isMax, isMin: boolean;
|
|
|
|
procedure SetColumnWidth(aCol,aWidth: Integer);
|
|
begin
|
|
if csLoading in ComponentState then
|
|
SetRawColWidths(aCol, aWidth)
|
|
else
|
|
SetColWidths(aCol, aWidth);
|
|
end;
|
|
|
|
procedure DeleteWidth(aIndex: Integer);
|
|
begin
|
|
if aIndex < Length(widths) - 1 then
|
|
move(widths[aIndex+1], widths[aIndex], (Length(widths)-aIndex-1) * SizeOf(widths[0]));
|
|
SetLength(widths, Length(widths) - 1);
|
|
end;
|
|
|
|
begin
|
|
if not AutoFillColumns then
|
|
exit;
|
|
|
|
if FUpdatingAutoFillCols then
|
|
exit;
|
|
|
|
FUpdatingAutoFillCols:=True;
|
|
try
|
|
// if needed, last size can be obtained from FLastWidth
|
|
// when InternalAutoFillColumns is called from DoChangeBounds
|
|
// for example.
|
|
|
|
// A simple algorithm is implemented:
|
|
// if SizePriority=0, column size should be unmodified
|
|
// if SizePriority<>0 means variable size column whose width
|
|
// is the average available size respecting each column
|
|
// MinSize and MaxSize constraints, such constraints
|
|
// are valid only if they are bigger than 0.
|
|
|
|
widths := nil;
|
|
SetLength(widths, ColCount);
|
|
|
|
availableSize := ClientWidth - GetBorderWidth;
|
|
for i:=ColCount-1 downto 0 do
|
|
with widths[i] do begin
|
|
aIndex := i;
|
|
GetAutoFillColumnInfo(i, aMin, aMax, aPriority);
|
|
aWidth := GetColWidths(i);
|
|
if aPriority=0 then begin
|
|
Dec(availableSize, aWidth);
|
|
DeleteWidth(i);
|
|
end
|
|
end;
|
|
|
|
if Length(widths)=0 then begin
|
|
// it's an autofillcolumns grid either WITHOUT custom colums and
|
|
// fixedCols=ColCount or WITH custom columns where all columns
|
|
// have PrioritySize=0, resize the last column (inherited behavior)
|
|
i := ColCount-1;
|
|
if (i>=FixedCols) then // aMax of last column ...
|
|
SetColumnWidth(i, AvailableSize + GetColWidths(i));
|
|
exit;
|
|
end;
|
|
|
|
avgSize := availableSize div Length(widths);
|
|
|
|
repeat
|
|
done := true;
|
|
for i:=Length(widths)-1 downto 0 do
|
|
with widths[i] do begin
|
|
isMax := ((aMax>0) and (avgSize>aMax));
|
|
isMin := ((aMin>0) and (avgSize<aMin));
|
|
if isMax or isMin then begin
|
|
if isMax then aWidth := aMax;
|
|
if isMin then aWidth := aMin;
|
|
SetColumnWidth(aIndex, aWidth);
|
|
availableSize := Max(availableSize-aWidth, 0);
|
|
DeleteWidth(i);
|
|
if length(widths)>0 then
|
|
avgSize := availableSize div length(widths);
|
|
done := false;
|
|
break;
|
|
end;
|
|
end;
|
|
until done;
|
|
|
|
if length(widths)>0 then begin
|
|
rest := availableSize mod length(widths);
|
|
for i:=0 to length(widths)-1 do
|
|
with widths[i] do begin
|
|
aWidth := Max(avgSize, 0);
|
|
if rest>0 then begin
|
|
inc(aWidth);
|
|
dec(rest);
|
|
end;
|
|
SetColumnWidth(aIndex, aWidth);
|
|
end;
|
|
end;
|
|
|
|
finally
|
|
FUpdatingAutoFillCols:=False;
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.InternalNeedBorder: boolean;
|
|
begin
|
|
result := FFlat and (FGridBorderStyle = bsSingle);
|
|
end;
|
|
|
|
procedure TCustomGrid.InternalSetColCount(ACount: Integer);
|
|
var
|
|
OldC: Integer;
|
|
NewRowCount: Integer;
|
|
begin
|
|
OldC := FCols.Count;
|
|
if ACount=OldC then
|
|
Exit;
|
|
if ACount<1 then
|
|
Clear
|
|
else begin
|
|
if EditorMode and (ACount<=Col) then
|
|
EditorMode:=False;
|
|
NewRowCount := RowCount;
|
|
if (OldC=0) and FGridPropBackup.ValidData then begin
|
|
NewRowCount := FGridPropBackup.RowCount;
|
|
FFixedRows := Min(FGridPropBackup.FixedRowCount, NewRowCount);
|
|
FFixedCols := Min(FGridPropBackup.FixedColCount, ACount);
|
|
end;
|
|
CheckFixedCount(ACount, NewRowCount, FFixedCols, FFixedRows);
|
|
CheckCount(ACount, NewRowCount);
|
|
AdjustCount(True, OldC, ACount);
|
|
FGridPropBackup.ValidData := false;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.InternalSetColWidths(aCol, aValue: Integer);
|
|
var
|
|
OldSize,NewSize: Integer;
|
|
R: TRect;
|
|
Bigger: boolean;
|
|
begin
|
|
NewSize := AValue;
|
|
if NewSize<0 then begin
|
|
AValue:=-1;
|
|
NewSize := DefaultColWidth;
|
|
end;
|
|
|
|
OldSize := FCols[ACol];
|
|
if NewSize<>OldSize then begin
|
|
|
|
if OldSize<0 then
|
|
OldSize := DefaultColWidth;
|
|
|
|
Bigger := NewSize>OldSize;
|
|
SetRawColWidths(ACol, AValue);
|
|
|
|
if not (csLoading in ComponentState) and HandleAllocated then begin
|
|
|
|
if FUpdateCount=0 then begin
|
|
UpdateSizes;
|
|
R := CellRect(aCol, 0);
|
|
R.Bottom := FGCache.MaxClientXY.Y+GetBorderWidth+1;
|
|
if UseRightToLeftAlignment then begin
|
|
//Bigger or not bigger i will refresh
|
|
R.Left := FGCache.ClientRect.Left;
|
|
if aCol=FTopLeft.x then
|
|
R.Right := FGCache.ClientRect.Right - FGCache.FixedWidth;
|
|
end
|
|
else begin
|
|
if Bigger then
|
|
R.Right := FGCache.MaxClientXY.X+GetBorderWidth+1
|
|
else
|
|
R.Right := FGCache.ClientWidth;
|
|
if aCol=FTopLeft.x then
|
|
R.Left := FGCache.FixedWidth;
|
|
end;
|
|
InvalidateRect(handle, @R, False);
|
|
end;
|
|
|
|
if (FEditor<>nil)and(Feditor.Visible)and(ACol<=FCol) then
|
|
EditorWidthChanged(aCol, aValue);
|
|
ColWidthsChanged;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.InternalUpdateColumnWidths;
|
|
var
|
|
i: Integer;
|
|
C: TGridColumn;
|
|
begin
|
|
for i:= FixedCols to ColCount-1 do begin
|
|
C := ColumnFromGridColumn(i);
|
|
if C<>nil then
|
|
SetRawColWidths(i, C.Width);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.InvalidateMovement(DCol, DRow: Integer; OldRange: TRect);
|
|
|
|
procedure doInvalidateRange(Col1,Row1,Col2,Row2: Integer);
|
|
begin
|
|
InvalidateRange(Rect(Col1,Row1,Col2,Row2));
|
|
end;
|
|
|
|
begin
|
|
if (goRowHighlight in Options) then
|
|
OldRange := Rect(FFixedCols, OldRange.Top, Colcount-1, OldRange.Bottom);
|
|
if SelectActive then begin
|
|
|
|
if DCol>FCol then begin
|
|
// expanded cols
|
|
if not (goRowSelect in Options) then
|
|
doInvalidateRange(FCol, OldRange.Top, DCol, Oldrange.Bottom)
|
|
|
|
else if (goRelaxedRowSelect in Options) and (DRow=FRow) then
|
|
InvalidateRow(DRow)
|
|
|
|
end else if DCol<FCol then begin
|
|
// shrunk cols
|
|
if not (goRowSelect in Options) then
|
|
doInvalidateRange(DCol,OldRange.Top,FCol,OldRange.Bottom)
|
|
|
|
else if (goRelaxedRowSelect in Options) and (DRow=FRow) then
|
|
InvalidateRow(DRow)
|
|
|
|
end;
|
|
|
|
if DRow>FRow then
|
|
// expanded rows
|
|
doInvalidateRange(OldRange.Left, FRow, OldRange.Right, DRow)
|
|
|
|
else if DRow<FRow then
|
|
// shrunk rows
|
|
doInvalidateRange(OldRange.Left, DRow, OldRange.Right, FRow);
|
|
|
|
if not ((goRowSelect in Options) or (goRowHighlight in Options)) then begin
|
|
|
|
// Above rules do work only if either rows or cols remain
|
|
// constant, if both rows and cols change there may be gaps
|
|
//
|
|
// four cases are left.
|
|
//
|
|
|
|
if (DCol>FCol)and(DRow<FRow) then // (1: I Cuadrant)
|
|
// Rect(FCol+1,FRow-1,DCol,DRow) normalized -v
|
|
doInvalidateRange(FCol+1, DRow, DCol, FRow-1)
|
|
else
|
|
if (DCol<FCol)and(DRow<FRow) then // (2: II Cuadrant)
|
|
// Rect(FCol-1,FRow-1,DCol,DRow) normalized -v
|
|
doInvalidateRange(DCol, DRow, FCol-1, FRow-1)
|
|
else
|
|
if (DCol<FCol)and(DRow>FRow) then // (3: III Cuadrant)
|
|
// Rect(FCol-1,FRow+1,DCol,DRow) normalized -v
|
|
doInvalidateRange(DCol, FRow+1, FCol-1, DRow)
|
|
else
|
|
if (DCol>FCol)and(DRow>FRow) then // (4: IV Cuadrant)
|
|
// normalization not needed
|
|
doInvalidateRange(FCol+1,FRow+1,DCol,DRow);
|
|
|
|
end;
|
|
|
|
end else begin
|
|
|
|
if (OldRange.Right-OldRange.Left>0) or
|
|
(OldRange.Bottom-OldRange.Top>0) then
|
|
// old selected range gone, invalidate old area
|
|
InvalidateRange(OldRange)
|
|
else
|
|
// Single cell
|
|
InvalidateCell(FCol, FRow);
|
|
|
|
// and invalidate current selecion, cell or full row
|
|
if ((goRowSelect in Options) or (goRowHighlight in Options)) then
|
|
InvalidateRow(Drow)
|
|
else
|
|
InvalidateCell(DCol, DRow);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
function TCustomGrid.IsColumnsStored: boolean;
|
|
begin
|
|
result := Columns.Enabled;
|
|
end;
|
|
|
|
function TCustomGrid.IsPushCellActive: boolean;
|
|
begin
|
|
with FGCache do
|
|
result := (PushedCell.X<>-1) and (PushedCell.Y<>-1);
|
|
end;
|
|
|
|
function TCustomGrid.LoadResBitmapImage(const ResName: string): TBitmap;
|
|
var
|
|
C: TPortableNetworkGraphic;
|
|
begin
|
|
C := TPortableNetworkGraphic.Create;
|
|
try
|
|
C.LoadFromResourceName(hInstance, ResName);
|
|
Result := TBitmap.Create;
|
|
Result.Assign(C);
|
|
finally
|
|
C.Free;
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.MouseButtonAllowed(Button: TMouseButton): boolean;
|
|
begin
|
|
result := (Button=mbLeft);
|
|
end;
|
|
|
|
function TCustomGrid.GetLeftCol: Integer;
|
|
begin
|
|
result:=fTopLeft.x;
|
|
end;
|
|
|
|
function TCustomGrid.GetPxTopLeft: TPoint;
|
|
begin
|
|
if (FTopLeft.x >= 0) and (FTopLeft.x < FGCache.AccumWidth.Count) then
|
|
Result.x := FGCache.AccumWidth[FTopLeft.x]+FGCache.TLColOff-FGCache.FixedWidth
|
|
else if FTopLeft.x > 0 then
|
|
Result.x := FGCache.GridWidth+FGCache.TLColOff-FGCache.FixedWidth
|
|
else
|
|
Result.x := 0;
|
|
|
|
if (FTopLeft.y >= 0) and (FTopLeft.y < FGCache.AccumHeight.Count) then
|
|
Result.y := FGCache.AccumHeight[FTopLeft.y]+FGCache.TLRowOff-FGCache.FixedHeight
|
|
else if FTopLeft.y > 0 then
|
|
Result.y := FGCache.GridHeight+FGCache.TLRowOff-FGCache.FixedHeight
|
|
else
|
|
Result.y := 0;
|
|
end;
|
|
|
|
function TCustomGrid.GetColCount: Integer;
|
|
begin
|
|
Result:=FCols.Count;
|
|
end;
|
|
|
|
function TCustomGrid.GetRowCount: Integer;
|
|
begin
|
|
Result:=FRows.Count;
|
|
end;
|
|
|
|
function TCustomGrid.IsColumnIndexValid(AIndex: Integer): boolean;
|
|
begin
|
|
Result := (AIndex>=0) and (AIndex<ColCount);
|
|
end;
|
|
|
|
function TCustomGrid.IsRowIndexValid(AIndex: Integer): boolean;
|
|
begin
|
|
Result := (AIndex>=0) and (AIndex<RowCount);
|
|
end;
|
|
|
|
function TCustomGrid.IsColumnIndexVariable(AIndex: Integer): boolean;
|
|
begin
|
|
Result := (AIndex>=FFixedCols) and (AIndex<ColCount);
|
|
end;
|
|
|
|
function TCustomGrid.IsRowIndexVariable(AIndex: Integer): boolean;
|
|
begin
|
|
Result := (AIndex>=FFixedRows) and (AIndex<RowCount);
|
|
end;
|
|
|
|
function TCustomGrid.GetColWidths(Acol: Integer): Integer;
|
|
var
|
|
C: TGridColumn;
|
|
begin
|
|
if not Columns.Enabled or (aCol<FirstGridColumn) then
|
|
begin
|
|
if IsColumnIndexValid(aCol) then
|
|
Result:=FCols[aCol]
|
|
else
|
|
Result:=-1;
|
|
end else
|
|
begin
|
|
C := ColumnFromGridColumn(Acol);
|
|
if C<>nil then
|
|
Result:=C.Width
|
|
else
|
|
Result:=-1;
|
|
end;
|
|
if Result<0 then
|
|
Result:=DefaultColWidth;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetEditor(AValue: TWinControl);
|
|
var
|
|
Msg: TGridMessage;
|
|
begin
|
|
if FEditor=AValue then exit;
|
|
|
|
{$ifdef DbgGrid}
|
|
DebugLnEnter('TCustomGrid.SetEditor %s oldEd=%s newEd=%s INIT',[dbgsName(self),dbgsName(FEditor),dbgsName(Avalue)]);
|
|
{$endif}
|
|
if (FEditor<>nil) and FEditor.Visible then
|
|
EditorHide;
|
|
|
|
FEditor:=AValue;
|
|
if FEditor<>nil then begin
|
|
|
|
if FEditor.Parent=nil then
|
|
FEditor.Visible:=False;
|
|
|
|
if FEditor.Parent<>Self then
|
|
FEditor.Parent:=Self;
|
|
|
|
Msg.LclMsg.msg:=GM_SETGRID;
|
|
Msg.Grid:=Self;
|
|
Msg.Options:=0;
|
|
FEditor.Dispatch(Msg);
|
|
|
|
FEditorOptions := Msg.Options + 1; // force new editor setup
|
|
SetEditorOptions(Msg.Options);
|
|
end;
|
|
{$ifdef DbgGrid}
|
|
DebugLnExit('TCustomGrid.SetEditor DONE');
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.SetFixedCols(const AValue: Integer);
|
|
begin
|
|
if FFixedCols=AValue then begin
|
|
if FixedGrid and FGridPropBackup.ValidData then begin
|
|
// user modified fixed properties in fixed grid
|
|
// update stored values
|
|
FGridPropBackup.FixedColCount := AValue;
|
|
end;
|
|
exit;
|
|
end;
|
|
CheckFixedCount(ColCount, RowCount, AValue, FFixedRows);
|
|
|
|
if EditorMode then
|
|
EditorMode:=False;
|
|
|
|
FFixedCols:=AValue;
|
|
FTopLeft.x:=AValue;
|
|
|
|
if Columns.Enabled then begin
|
|
|
|
FCol:=AValue;
|
|
UpdateSelectionRange;
|
|
if not (csLoading in componentState) then
|
|
doTopleftChange(true);
|
|
|
|
ColumnsChanged(nil)
|
|
|
|
end else begin
|
|
|
|
if not (csLoading in componentState) then
|
|
doTopleftChange(true);
|
|
|
|
MoveNextSelectable(False, FixedCols, FRow);
|
|
UpdateSelectionRange;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetFixedRows(const AValue: Integer);
|
|
begin
|
|
if FFixedRows=AValue then begin
|
|
if FixedGrid and FGridPropBackup.ValidData then begin
|
|
// user modified fixed properties in fixed grid
|
|
// update stored values
|
|
FGridPropBackup.FixedRowCount := AValue;
|
|
end;
|
|
exit;
|
|
end;
|
|
CheckFixedCount(ColCount, RowCount, FFixedCols, AValue);
|
|
|
|
if EditorMode then
|
|
EditorMode:=False;
|
|
|
|
FFixedRows:=AValue;
|
|
FTopLeft.y:=AValue;
|
|
|
|
if not (csLoading in ComponentState) then
|
|
doTopleftChange(true);
|
|
|
|
MoveNextSelectable(False, FCol, FixedRows);
|
|
UpdateSelectionRange;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetGridLineColor(const AValue: TColor);
|
|
begin
|
|
if FGridLineColor=AValue then exit;
|
|
FGridLineColor:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetFixedGridLineColor(const AValue: TColor);
|
|
begin
|
|
if FFixedGridLineColor=AValue then exit;
|
|
FFixedGridLineColor:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetLeftCol(const AValue: Integer);
|
|
begin
|
|
TryScrollTo(AValue, FTopLeft.Y, True, False);
|
|
end;
|
|
|
|
procedure TCustomGrid.SetOptions(const AValue: TGridOptions);
|
|
begin
|
|
if FOptions=AValue then exit;
|
|
FOptions:=AValue;
|
|
UpdateSelectionRange;
|
|
if goEditing in Options then
|
|
SelectEditor;
|
|
if goAlwaysShowEditor in Options then
|
|
EditorShow(true)
|
|
else
|
|
EditorHide;
|
|
if goAutoAddRowsSkipContentCheck in Options then
|
|
FRowAutoInserted := False;
|
|
VisualChange;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetOptions2(const AValue: TGridOptions2);
|
|
begin
|
|
if FOptions2=AValue then exit;
|
|
FOptions2:=AValue;
|
|
VisualChange;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetScrollBars(const AValue: TScrollStyle);
|
|
begin
|
|
if FScrollBars=AValue then exit;
|
|
FScrollBars:=AValue;
|
|
VisualChange;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetTopRow(const AValue: Integer);
|
|
begin
|
|
TryScrollTo(FTopLeft.X, Avalue, False, True);
|
|
end;
|
|
|
|
function TCustomGrid.StartColSizing(const X, Y: Integer):boolean;
|
|
var
|
|
OrgIndex, TmpIndex: Integer;
|
|
ACase: Integer;
|
|
begin
|
|
|
|
result := false;
|
|
with FSizing do begin
|
|
|
|
OrgIndex := FGCache.ClickCell.X;
|
|
if OrgIndex<0 then begin
|
|
// invalid starting cell
|
|
if not AllowOutBoundEvents and (FCursorState=gcsColWidthChanging) then
|
|
// resizing still allowed if mouse is within "resizeable region"
|
|
OrgIndex := Index
|
|
else
|
|
exit;
|
|
end;
|
|
|
|
Index := OrgIndex;
|
|
ColRowToOffset(true, true, Index, OffIni, OffEnd);
|
|
|
|
if (Min(OffEnd, FGCache.ClientRect.Right)-FGCache.ClickMouse.X) < (FGCache.ClickMouse.X-OffIni) then begin
|
|
if X>FGCache.ClickMouse.X then
|
|
ACase := 4 // dragging right side to the right
|
|
else
|
|
ACase := 3; // dragging right side to the left
|
|
end else begin
|
|
if X>FGCache.ClickMouse.X then
|
|
ACase := 2 // dragging left side to the right
|
|
else
|
|
ACase := 1; // dragging left side to the left
|
|
end;
|
|
|
|
if UseRightToLeftAlignment then begin
|
|
case ACase of
|
|
1: ACase := 4;
|
|
2: ACase := 3;
|
|
3: ACase := 2;
|
|
4: ACase := 1;
|
|
end;
|
|
end;
|
|
|
|
case ACase of
|
|
3: ; // current column is the right one to resize
|
|
4: // find following covered column (visible 0-width) at the right side
|
|
begin
|
|
TmpIndex := Index;
|
|
while (TmpIndex<ColCount-1) and (ColWidths[TmpIndex+1]=0) do begin
|
|
Inc(TmpIndex);
|
|
if not Columns.Enabled or ColumnFromGridColumn(TmpIndex).Visible then
|
|
Index := TmpIndex;
|
|
end;
|
|
end;
|
|
2: // find previous visible (width>0) or covered column
|
|
begin
|
|
Dec(Index);
|
|
while (Index>FixedCols) do begin
|
|
if not Columns.Enabled or ColumnFromGridColumn(Index).Visible then
|
|
break;
|
|
Dec(Index);
|
|
end;
|
|
end;
|
|
1: // find previous visible (width>0) column
|
|
begin
|
|
Dec(Index);
|
|
while (Index>FixedCols) do begin
|
|
if ColWidths[Index]>0 then
|
|
break;
|
|
Dec(Index);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if OrgIndex<>Index then
|
|
ColRowToOffset(True, True, Index, OffIni, OffEnd);
|
|
|
|
// if precision on changing cursor from normal to split is expanded, there
|
|
// will be a starting big jump on size, to fix it, uncomment next lines
|
|
// TODO: check for RTL
|
|
//DeltaOff := OffEnd - FGCache.ClickMouse.X;
|
|
DeltaOff := 0;
|
|
|
|
if goFixedColSizing in Options then
|
|
result := (Index>=0)
|
|
else
|
|
result := (Index>=FixedCols);
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TCustomGrid.ChangeCursor(ACursor: TCursor;
|
|
ASaveCurrentCursor: Boolean = true);
|
|
begin
|
|
if FCursorChangeLock = 0 then
|
|
begin
|
|
if ASaveCurrentCursor then
|
|
FSavedCursor := Cursor;
|
|
inc(FCursorChangeLock);
|
|
Cursor := ACursor;
|
|
dec(FCursorChangeLock);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.RestoreCursor;
|
|
begin
|
|
Cursor := FSavedCursor;
|
|
FCursorState := gcsDefault;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetRowHeights(Arow: Integer; Avalue: Integer);
|
|
var
|
|
OldSize,NewSize: Integer;
|
|
R: TRect;
|
|
Bigger: boolean;
|
|
begin
|
|
|
|
NewSize := AValue;
|
|
if NewSize<0 then begin
|
|
AValue:=-1;
|
|
NewSize := DefaultRowHeight;
|
|
end;
|
|
|
|
OldSize := FRows[ARow];
|
|
if AValue<>OldSize then begin
|
|
|
|
if OldSize<0 then
|
|
OldSize := DefaultRowHeight;
|
|
|
|
bigger := NewSize > OldSize;
|
|
|
|
FRows[ARow]:=AValue;
|
|
|
|
if not (csLoading in ComponentState) and HandleAllocated then begin
|
|
if FUpdateCount=0 then begin
|
|
UpdateSizes;
|
|
|
|
R := CellRect(0, aRow);
|
|
if UseRightToLeftAlignment then
|
|
begin
|
|
R.Left := FlipX(FGCache.MaxClientXY.X+GetBorderWidth);
|
|
R.Right := R.Right + 1;
|
|
end
|
|
else
|
|
R.Right := FGCache.MaxClientXY.X+GetBorderWidth+1;
|
|
if bigger then
|
|
R.Bottom := FGCache.MaxClientXY.Y+GetBorderWidth+1
|
|
else
|
|
R.Bottom := FGCache.ClientHeight;
|
|
if aRow=FTopLeft.y then
|
|
R.Top := FGCache.FixedHeight;
|
|
|
|
InvalidateRect(handle, @R, False);
|
|
end;
|
|
|
|
if (FEditor<>nil)and(Feditor.Visible)and(ARow<=FRow) then EditorPos;
|
|
RowHeightsChanged;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetColWidths(Acol: Integer; Avalue: Integer);
|
|
var
|
|
c: TGridColumn;
|
|
OldWidth: Integer;
|
|
begin
|
|
if not Columns.Enabled or (aCol<FFixedCols) then
|
|
internalSetColWidths(aCol, aValue)
|
|
else begin
|
|
C := ColumnFromGridColumn(ACol);
|
|
if C<>nil then begin
|
|
OldWidth := C.Width;
|
|
C.Width := AValue;
|
|
SetRawColWidths(ACol, AValue);
|
|
if OldWidth<>C.Width then
|
|
EditorWidthChanged(aCol, C.Width);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetRawColWidths(ACol: Integer; AValue: Integer);
|
|
begin
|
|
if ACol < FCols.Count then // Prevent a range error in case of a bug.
|
|
FCols[ACol]:=Avalue
|
|
else
|
|
DebugLn(['TCustomGrid.SetRawColWidths with Range Error: ACol=', ACol, ', Cols.Count=', FCols.Count]);
|
|
end;
|
|
|
|
procedure TCustomGrid.AdjustCount(IsColumn: Boolean; OldValue, NewValue: Integer);
|
|
|
|
procedure AddDel(Lst: TIntegerList; aCount: Integer);
|
|
begin
|
|
while lst.Count<aCount do
|
|
Lst.Add(-1); // default width/height
|
|
Lst.Count:=aCount;
|
|
end;
|
|
|
|
var
|
|
OldCount, NewCount: integer;
|
|
begin
|
|
if IsColumn then begin
|
|
AddDel(FCols, NewValue);
|
|
FGCache.AccumWidth.Count:=NewValue;
|
|
OldCount:=RowCount;
|
|
if (OldValue=0)and(NewValue>=0) then begin
|
|
FTopLeft.X:=FFixedCols;
|
|
if RowCount=0 then begin
|
|
if FGridPropBackup.ValidData then begin
|
|
NewCount := FGridPropBackup.RowCount;
|
|
FFixedRows := Min(FGridPropBackup.FixedRowCount, NewCount);
|
|
end
|
|
else
|
|
NewCount := 1;
|
|
FTopLeft.Y:=FFixedRows;
|
|
AddDel(FRows, NewCount);
|
|
FGCache.AccumHeight.Count:=NewCount;
|
|
end;
|
|
end;
|
|
UpdateCachedSizes;
|
|
SizeChanged(OldValue, OldCount);
|
|
// if new count makes current col out of range, adjust position
|
|
// if not, position should not change (fake changed col to be the last one)
|
|
Dec(NewValue);
|
|
if NewValue<Col then
|
|
NewValue:=Col;
|
|
FixPosition(True, NewValue);
|
|
end else begin
|
|
AddDel(FRows, NewValue);
|
|
FGCache.AccumHeight.Count:=NewValue;
|
|
OldCount:=ColCount;
|
|
if (OldValue=0)and(NewValue>=0) then begin
|
|
FTopleft.Y:=FFixedRows;
|
|
//DebugLn('TCustomGrid.AdjustCount B ',DbgSName(Self),' FTopLeft=',dbgs(FTopLeft));
|
|
if FCols.Count=0 then begin
|
|
if FGridPropBackup.ValidData then begin
|
|
NewCount := FGridPropBackup.ColCount;
|
|
FFixedCols := Min(FGridPropBackup.FixedColCount, NewCount);
|
|
end
|
|
else begin
|
|
NewCount := 1;
|
|
FFixedCols := 0;
|
|
end;
|
|
FTopLeft.X:=FFixedCols;
|
|
AddDel(FCols, NewCount);
|
|
FGCache.AccumWidth.Count:=NewCount;
|
|
end;
|
|
end;
|
|
UpdateCachedSizes;
|
|
SizeChanged(OldCount, OldValue);
|
|
// if new count makes current row out of range, adjust position
|
|
// if not, position should not change (fake changed row to be the last one)
|
|
Dec(NewValue);
|
|
if NewValue<Row then
|
|
NewValue:=Row;
|
|
FixPosition(False, NewValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.AdjustEditorBounds(NewCol,NewRow:Integer);
|
|
begin
|
|
SetColRow(NewCol,NewRow);
|
|
if EditorMode then
|
|
EditorPos;
|
|
end;
|
|
|
|
procedure TCustomGrid.AfterMoveSelection(const prevCol, prevRow: Integer);
|
|
begin
|
|
if Assigned(OnAfterSelection) then
|
|
OnAfterSelection(Self, prevCol, prevRow);
|
|
end;
|
|
|
|
procedure TCustomGrid.AssignTo(Dest: TPersistent);
|
|
var
|
|
Target: TCustomGrid;
|
|
begin
|
|
if Dest is TCustomGrid then begin
|
|
|
|
Target := TCustomGrid(Dest);
|
|
Target.BeginUpdate;
|
|
|
|
// structure
|
|
Target.FixedCols := 0;
|
|
Target.FixedRows := 0;
|
|
if Columns.Enabled then
|
|
Target.Columns.Assign(Columns)
|
|
else begin
|
|
Target.ColCount :=ColCount;
|
|
end;
|
|
Target.RowCount := RowCount;
|
|
Target.FixedCols := FixedCols;
|
|
Target.FixedRows := FixedRows;
|
|
if DefaultRowHeightIsStored then
|
|
Target.DefaultRowHeight := DefaultRowHeight
|
|
else
|
|
Target.DefaultRowHeight := -1;
|
|
if DefaultColWidthIsStored then
|
|
Target.DefaultColWidth := DefaultColWidth
|
|
else
|
|
Target.DefaultColWidth := -1;
|
|
if not Columns.Enabled then
|
|
Target.FCols.Assign(FCols);
|
|
Target.FRows.Assign(FRows);
|
|
|
|
// Options
|
|
Target.Options := Options;
|
|
Target.Color := Color;
|
|
Target.FixedColor := FixedColor;
|
|
Target.AlternateColor := AlternateColor;
|
|
Target.Font := Font;
|
|
Target.TitleFont := TitleFont;
|
|
|
|
// position
|
|
Target.TopRow := TopRow;
|
|
Target.LeftCol := LeftCol;
|
|
Target.Col := Col;
|
|
Target.Row := Row;
|
|
Target.FRange := FRange;
|
|
|
|
Target.EndUpdate;
|
|
|
|
end else
|
|
inherited AssignTo(Dest);
|
|
end;
|
|
|
|
procedure TCustomGrid.SetColCount(AValue: Integer);
|
|
begin
|
|
if Columns.Enabled then
|
|
raise EGridException.Create('Use Columns property to add/remove columns');
|
|
InternalSetColCount(AValue);
|
|
end;
|
|
|
|
procedure TCustomGrid.SetRowCount(AValue: Integer);
|
|
var
|
|
OldR, NewColCount: Integer;
|
|
begin
|
|
OldR := FRows.Count;
|
|
if AValue<>OldR then begin
|
|
if AValue>=0 then begin
|
|
if EditorMode and (AValue<=Row) then
|
|
EditorMode:=False;
|
|
NewColCount := ColCount;
|
|
if (OldR=0) and FGridPropBackup.ValidData then begin
|
|
NewColCount := FGridPropBackup.ColCount;
|
|
FFixedCols := Min(FGridPropBackup.FixedColCount, NewColCount);
|
|
FFixedRows := Min(FGridPropBackup.FixedRowCount, AValue);
|
|
FTopLeft.X := FFixedCols;
|
|
FTopLeft.Y := FFixedRows;
|
|
// ignore backedup value of rowcount because
|
|
// finally rowcount will be AValue
|
|
FGridPropBackup.RowCount := AValue;
|
|
end;
|
|
if Columns.Enabled then begin
|
|
// setup custom columns
|
|
Self.ColumnsChanged(nil);
|
|
FGridPropBackup.ValidData := false;
|
|
// still need to adjust rowcount?
|
|
if AValue=FRows.Count then
|
|
exit;
|
|
end;
|
|
CheckFixedCount(NewColCount, AValue, FFixedCols, FFixedRows);
|
|
CheckCount(NewColCount, AValue);
|
|
AdjustCount(False, OldR, AValue);
|
|
end
|
|
else
|
|
ClearRows;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetDefColWidth(AValue: Integer);
|
|
var
|
|
OldLeft,OldRight,NewLeft,NewRight: Integer;
|
|
begin
|
|
if AValue=fDefColwidth then
|
|
Exit;
|
|
FDefColWidth:=AValue;
|
|
FRealizedDefColWidth := 0;
|
|
|
|
if EditorMode then
|
|
ColRowToOffset(True, True, FCol, OldLeft, OldRight);
|
|
|
|
ResetDefaultColWidths;
|
|
|
|
if EditorMode then begin
|
|
ColRowToOffset(True, True, FCol, NewLeft, NewRight);
|
|
if (NewLeft<>OldLeft) or (NewRight<>OldRight) then
|
|
EditorWidthChanged(FCol, GetColWidths(FCol));
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetDefRowHeight(AValue: Integer);
|
|
var
|
|
i: Integer;
|
|
OldTop,OldBottom,NewTop,NewBottom: Integer;
|
|
begin
|
|
if (AValue<>fDefRowHeight) or (csLoading in ComponentState) then
|
|
begin
|
|
FDefRowheight:=AValue;
|
|
FRealizedDefRowHeight := 0;
|
|
|
|
if EditorMode then
|
|
ColRowToOffSet(False,True, FRow, OldTop, OldBottom);
|
|
|
|
for i:=0 to RowCount-1 do
|
|
FRows[i] := -1;
|
|
VisualChange;
|
|
|
|
if EditorMode then
|
|
begin
|
|
ColRowToOffSet(False,True, FRow, NewTop, NewBottom);
|
|
if (NewTop<>OldTOp) or (NewBottom<>OldBottom) then
|
|
EditorPos;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetCol(AValue: Integer);
|
|
begin
|
|
if AValue=FCol then Exit;
|
|
if not AllowOutboundEvents then
|
|
CheckLimitsWithError(AValue, FRow);
|
|
MoveExtend(False, AValue, FRow, True);
|
|
Click;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetRangeSelectMode(const AValue: TRangeSelectMode);
|
|
begin
|
|
if FRangeSelectMode=AValue then exit;
|
|
FRangeSelectMode := AValue;
|
|
ClearSelections;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetRow(AValue: Integer);
|
|
begin
|
|
if AValue=FRow then Exit;
|
|
if not AllowOutBoundEvents then
|
|
CheckLimitsWithError(FCol, AValue);
|
|
MoveExtend(False, FCol, AValue, True);
|
|
Click;
|
|
end;
|
|
|
|
procedure TCustomGrid.Sort(ColSorting: Boolean; index, IndxFrom, IndxTo: Integer);
|
|
procedure QuickSort(L,R: Integer);
|
|
var
|
|
I,J: Integer;
|
|
P{,Q}: Integer;
|
|
begin
|
|
repeat
|
|
I:=L;
|
|
J:=R;
|
|
P:=(L+R) div 2;
|
|
repeat
|
|
if ColSorting then begin
|
|
while DoCompareCells(index, P, index, I)>0 do I:=I+1;
|
|
while DoCompareCells(index, P, index, J)<0 do J:=J-1;
|
|
end else begin
|
|
while DoCompareCells(P, index, I, index)>0 do I:=I+1;
|
|
while DoCompareCells(P, index, J, index)<0 do J:=J-1;
|
|
end;
|
|
if I<=J then begin
|
|
|
|
if I<>J then
|
|
if not FStrictSort or
|
|
(ColSorting and (DoCompareCells(index, I, index, J)<>0)) or
|
|
(not ColSorting and (DoCompareCells(I, index, J, index)<>0))
|
|
then
|
|
DoOPExchangeColRow(not ColSorting, I,J);
|
|
|
|
if P=I then
|
|
P:=J
|
|
else if P=J then
|
|
P:=I;
|
|
|
|
I:=I+1;
|
|
J:=J-1;
|
|
end;
|
|
until I>J;
|
|
|
|
if L<J then
|
|
QuickSort(L,J);
|
|
|
|
L:=I;
|
|
until I>=R;
|
|
end;
|
|
begin
|
|
if RowCount>FixedRows then begin
|
|
CheckIndex(ColSorting, Index);
|
|
CheckIndex(not ColSorting, IndxFrom);
|
|
CheckIndex(not ColSorting, IndxTo);
|
|
BeginUpdate;
|
|
QuickSort(IndxFrom, IndxTo);
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.HideSortArrow;
|
|
begin
|
|
FSortColumn := -1;
|
|
InvalidateGrid;
|
|
end;
|
|
|
|
procedure TCustomGrid.doTopleftChange(DimChg: Boolean);
|
|
begin
|
|
TopLeftChanged;
|
|
VisualChange;
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawXORVertLine(X: Integer);
|
|
var
|
|
OldPenMode: TPenMode;
|
|
OldPenColor: TColor;
|
|
begin
|
|
OldPenMode := Canvas.Pen.Mode;
|
|
OldPenColor := Canvas.Pen.Color;
|
|
Canvas.Pen.Color := clWhite;
|
|
Canvas.Pen.Mode := pmXOR;
|
|
Canvas.MoveTo(X,0);
|
|
Canvas.LineTo(X,FGCache.MaxClientXY.Y);
|
|
Canvas.Pen.Mode := OldPenMode;
|
|
Canvas.Pen.Color := OldPenColor;
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawXORHorzLine(Y: Integer);
|
|
var
|
|
OldPenMode: TPenMode;
|
|
OldPenColor: TColor;
|
|
begin
|
|
OldPenMode := Canvas.Pen.Mode;
|
|
OldPenColor := Canvas.Pen.Color;
|
|
Canvas.Pen.Color := clWhite;
|
|
Canvas.Pen.Mode := pmXOR;
|
|
if UseRightToLeftAlignment then begin
|
|
Canvas.MoveTo(FlipX(FGCache.MaxClientXY.X)+1,Y);
|
|
Canvas.LineTo(FGCache.ClientRect.Right,Y);
|
|
end
|
|
else begin
|
|
Canvas.MoveTo(0,Y);
|
|
Canvas.LineTo(FGCache.MaxClientXY.X,Y);
|
|
end;
|
|
Canvas.Pen.Mode := OldPenMode;
|
|
Canvas.Pen.Color := OldPenColor;
|
|
end;
|
|
|
|
procedure TCustomGrid.VisualChange;
|
|
begin
|
|
if (FUpdateCount<>0) then
|
|
exit;
|
|
|
|
{$ifdef DbgVisualChange}
|
|
DebugLn('TCustomGrid.VisualChange INIT ',DbgSName(Self));
|
|
{$endif}
|
|
|
|
UpdateSizes;
|
|
|
|
Invalidate;
|
|
{$ifdef DbgVisualChange}
|
|
DebugLn('TCustomGrid.VisualChange END ',DbgSName(Self));
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.ResetSizes;
|
|
begin
|
|
//DebugLn('TCustomGrid.VisualChange ',DbgSName(Self));
|
|
if (FCols=nil) or ([csLoading,csDestroying]*ComponentState<>[])
|
|
or (not HandleAllocated) then
|
|
exit; // not yet initialized or already destroyed
|
|
|
|
UpdateCachedSizes;
|
|
CheckNewCachedSizes(FGCache);
|
|
CacheVisibleGrid;
|
|
{$Ifdef DbgVisualChange}
|
|
DebugLn('TCustomGrid.ResetSizes %s Width=%d Height=%d',[DbgSName(Self),Width,Height]);
|
|
DebugLn(' Cache: ClientWidth=%d ClientHeight=%d GWidth=%d GHeight=%d',
|
|
[FGCAche.ClientWidth, FGCache.ClientHeight,FGCache.GridWidth, FGCache.GridHeight]);
|
|
DebugLn(' Reald: ClientWidth=%d ClientHeight=%d',[ClientWidth, ClientHeight]);
|
|
DebugLn(' MaxTopLeft',dbgs(FGCache.MaxTopLeft));
|
|
{$Endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.CreateParams(var Params: TCreateParams);
|
|
const
|
|
ClassStylesOff = CS_VREDRAW or CS_HREDRAW;
|
|
begin
|
|
inherited CreateParams(Params);
|
|
with Params do begin
|
|
WindowClass.Style := WindowClass.Style and DWORD(not ClassStylesOff);
|
|
Style := Style or WS_VSCROLL or WS_HSCROLL or WS_CLIPCHILDREN;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.Click;
|
|
begin
|
|
{$IFDEF dbgGrid} DebugLn('FIgnoreClick=', dbgs(FIgnoreClick)); {$ENDIF}
|
|
if not FIgnoreClick then
|
|
inherited Click;
|
|
end;
|
|
|
|
procedure TCustomGrid.ScrollBarRange(Which: Integer; aRange,aPage,aPos: Integer);
|
|
var
|
|
ScrollInfo: TScrollInfo;
|
|
begin
|
|
if HandleAllocated then begin
|
|
{$Ifdef DbgScroll}
|
|
DebugLn('ScrollbarRange: Which=%s Range=%d Page=%d Pos=%d',
|
|
[SbToStr(Which),aRange,aPage,aPos]);
|
|
{$endif}
|
|
FillChar(ScrollInfo, SizeOf(ScrollInfo), 0);
|
|
ScrollInfo.cbSize := SizeOf(ScrollInfo);
|
|
ScrollInfo.fMask := SIF_RANGE or SIF_PAGE or SIF_DISABLENOSCROLL;
|
|
if not (gfPainting in FGridFlags) then
|
|
ScrollInfo.fMask := ScrollInfo.fMask or SIF_POS;
|
|
{$ifdef Unix}
|
|
ScrollInfo.fMask := ScrollInfo.fMask or SIF_UPDATEPOLICY;
|
|
if goThumbTracking in Options then
|
|
ScrollInfo.ntrackPos := SB_POLICY_CONTINUOUS
|
|
else
|
|
ScrollInfo.ntrackPos := SB_POLICY_DISCONTINUOUS;
|
|
{$endif}
|
|
ScrollInfo.nMin := 0;
|
|
ScrollInfo.nMax := aRange;
|
|
ScrollInfo.nPos := aPos;
|
|
if APage<0 then
|
|
APage := 0;
|
|
ScrollInfo.nPage := APage;
|
|
if (Which=SB_HORZ) and UseRightToLeftAlignment then begin
|
|
ScrollInfo.nPos := ScrollInfo.nMax-ScrollInfo.nPage-ScrollInfo.nPos;
|
|
{$Ifdef DbgScroll}
|
|
DebugLn('ScrollbarRange: RTL nPos=%d',[ScrollInfo.nPos]);
|
|
{$endif}
|
|
end;
|
|
SetScrollInfo(Handle, Which, ScrollInfo, True);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.ScrollBarPosition(Which, Value: integer);
|
|
var
|
|
ScrollInfo: TScrollInfo;
|
|
Vis: Boolean;
|
|
begin
|
|
if HandleAllocated then begin
|
|
{$Ifdef DbgScroll}
|
|
DebugLn('ScrollbarPosition: Which=',SbToStr(Which), ' Value= ',IntToStr(Value));
|
|
{$endif}
|
|
Vis := ScrollBarIsVisible(Which);
|
|
FillChar(ScrollInfo, SizeOf(ScrollInfo), 0);
|
|
ScrollInfo.cbSize := SizeOf(ScrollInfo);
|
|
if (Which=SB_HORZ) and Vis and UseRightToLeftAlignment then begin
|
|
ScrollInfo.fMask := SIF_PAGE or SIF_RANGE;
|
|
GetScrollInfo(Handle, SB_HORZ, ScrollInfo);
|
|
Value := (ScrollInfo.nMax-ScrollInfo.nPage)-Value;
|
|
{$Ifdef DbgScroll}
|
|
DebugLn('ScrollbarPosition: Which=',SbToStr(Which), ' RTL Value= ',IntToStr(Value));
|
|
{$endif}
|
|
end;
|
|
ScrollInfo.fMask := SIF_POS;
|
|
ScrollInfo.nPos:= Value;
|
|
SetScrollInfo(Handle, Which, ScrollInfo, Vis);
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.ScrollBarIsVisible(Which: Integer): Boolean;
|
|
begin
|
|
Result:=false;
|
|
if HandleAllocated then begin
|
|
// Don't use GetScrollbarvisible from the widgetset - it sends WM_PAINT message (Gtk2). Issue #30160
|
|
if Which = SB_VERT then result := (FVSbVisible=1) else
|
|
if Which = SB_HORZ then result := (FHsbVisible=1) else
|
|
if Which = SB_BOTH then result := (FVSbVisible=1) and (FHsbVisible=1);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.ScrollBarPage(Which: Integer; aPage: Integer);
|
|
var
|
|
ScrollInfo: TScrollInfo;
|
|
begin
|
|
if HandleAllocated then begin
|
|
{$Ifdef DbgScroll}
|
|
DebugLn('Scrollbar Page: Which=',SbToStr(Which), ' Avalue=',dbgs(aPage));
|
|
{$endif}
|
|
ScrollInfo.cbSize := SizeOf(ScrollInfo);
|
|
ScrollInfo.fMask := SIF_PAGE;
|
|
ScrollInfo.nPage:= aPage;
|
|
SetScrollInfo(Handle, Which, ScrollInfo, True);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.ScrollBarShow(Which: Integer; aValue: boolean);
|
|
begin
|
|
if HandleAllocated then begin
|
|
{$Ifdef DbgScroll}
|
|
DebugLn('ScrollbarShow: Which=',SbToStr(Which), ' Avalue=',dbgs(AValue));
|
|
{$endif}
|
|
Include(FGridFlags, gfUpdatingScrollbar);
|
|
try
|
|
ShowScrollBar(Handle,Which,aValue);
|
|
finally
|
|
Exclude(FGridFlags, gfUpdatingScrollbar);
|
|
end;
|
|
if Which in [SB_BOTH, SB_VERT] then FVSbVisible := Ord(AValue);
|
|
if Which in [SB_BOTH, SB_HORZ] then FHSbVisible := Ord(AValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.ScrollBy(DeltaX, DeltaY: Integer);
|
|
var
|
|
ClipArea: TRect;
|
|
ScrollFlags: Integer;
|
|
begin
|
|
if (DeltaX=0) and (DeltaY=0) then
|
|
Exit;
|
|
|
|
ScrollFlags := SW_INVALIDATE or SW_ERASE;
|
|
if DeltaX<>0 then
|
|
begin
|
|
ClipArea := ClientRect;
|
|
if Flat then
|
|
InflateRect(ClipArea, -1, -1);
|
|
if BiDiMode <> bdRightToLeft then
|
|
Inc(ClipArea.Left, FGCache.FixedWidth)
|
|
else
|
|
Dec(ClipArea.Right, FGCache.FixedWidth);
|
|
ScrollWindowEx(Handle, DeltaX, 0, @ClipArea, @ClipArea, 0, nil, ScrollFlags);
|
|
end;
|
|
if DeltaY<>0 then
|
|
begin
|
|
ClipArea := ClientRect;
|
|
if Flat then
|
|
InflateRect(ClipArea, -1, -1);
|
|
Inc(ClipArea.Top, FGCache.FixedHeight);
|
|
ScrollWindowEx(Handle, 0, DeltaY, @ClipArea, @ClipArea, 0, nil, ScrollFlags);
|
|
end;
|
|
|
|
CacheVisibleGrid;
|
|
CalcScrollbarsRange;
|
|
end;
|
|
|
|
function TCustomGrid.ScrollBarAutomatic(Which: TScrollStyle): boolean;
|
|
begin
|
|
result:=false;
|
|
if (Which=ssVertical)or(Which=ssHorizontal) then begin
|
|
if Which=ssVertical then Which:=ssAutoVertical
|
|
else Which:=ssAutoHorizontal;
|
|
Result:= FScrollBars in [Which, ssAutoBoth];
|
|
end;
|
|
end;
|
|
|
|
// Returns a rectagle corresponding to a physical cell[aCol,aRow]
|
|
function TCustomGrid.CellRect(ACol, ARow: Integer): TRect;
|
|
var
|
|
ok: Boolean;
|
|
begin
|
|
ok := ColRowToOffset(True, True, ACol, Result.Left, Result.Right);
|
|
if ok then begin
|
|
ok := ColRowToOffSet(False, True, ARow, Result.Top, Result.Bottom);
|
|
if ok and (goColSpanning in Options) then
|
|
CalcCellExtent(ACol, ARow, Result);
|
|
end;
|
|
|
|
if not ok then
|
|
Result:=Rect(0,0,0,0);
|
|
end;
|
|
|
|
// The visible grid Depends on TopLeft and ClientWidth,ClientHeight,
|
|
// Col/Row Count, So it Should be called inmediately after changing
|
|
// those properties.
|
|
function TCustomGrid.GetVisibleGrid: TRect;
|
|
var
|
|
W, H: Integer;
|
|
begin
|
|
|
|
if (FTopLeft.X<0)or(FTopLeft.y<0)or(csLoading in ComponentState) then begin
|
|
Result := Rect(0,0,-1,-1);
|
|
FGCache.MaxClientXY := point(0,0);
|
|
Exit;
|
|
end;
|
|
// visible TopLeft Cell
|
|
Result.TopLeft:=fTopLeft;
|
|
Result.BottomRight:=Result.TopLeft;
|
|
|
|
// Left Margin of next visible Column and Rightmost visible cell
|
|
if ColCount>FixedCols then begin
|
|
W:=GetColWidths(Result.Left) + FGCache.FixedWidth;
|
|
if GetSmoothScroll(SB_Horz) then
|
|
W := W - FGCache.TLColOff;
|
|
while (Result.Right<ColCount-1)and(W<FGCache.ClientWidth) do begin
|
|
Inc(Result.Right);
|
|
W:=W+GetColWidths(Result.Right);
|
|
end;
|
|
FGCache.MaxClientXY.X := W;
|
|
end else begin
|
|
FGCache.MaxClientXY.X := FGCache.FixedWidth;
|
|
Result.Right := Result.Left - 1; // no visible cells here
|
|
end;
|
|
|
|
// Top Margin of next visible Row and Bottom most visible cell
|
|
if RowCount>FixedRows then begin
|
|
H:=GetRowheights(Result.Top) + FGCache.FixedHeight;
|
|
if GetSmoothScroll(SB_Vert) then
|
|
H := H - FGCache.TLRowOff;
|
|
while (Result.Bottom<RowCount-1)and(H<FGCache.ClientHeight) do begin
|
|
Inc(Result.Bottom);
|
|
H:=H+GetRowHeights(Result.Bottom);
|
|
end;
|
|
FGCache.MaxClientXY.Y := H;
|
|
end else begin
|
|
FGCache.MaxClientXY.Y := FGCache.FixedHeight;
|
|
Result.Bottom := Result.Top - 1; // no visible cells here
|
|
end;
|
|
end;
|
|
|
|
{ Scroll the grid until cell[aCol,aRow] is shown }
|
|
function TCustomGrid.ScrollToCell(const aCol, aRow: Integer;
|
|
const ForceFullyVisible: Boolean): Boolean;
|
|
var
|
|
RNew, RNewStored: TRect;
|
|
OldTopLeft:TPoint;
|
|
Xinc,YInc: Integer;
|
|
CHeight,CWidth: Integer;
|
|
TLRowOffChanged, TLColOffChanged: Boolean;
|
|
begin
|
|
OldTopLeft:=fTopLeft;
|
|
TLRowOffChanged:=False;
|
|
TLColOffChanged:=False;
|
|
|
|
CHeight := FGCache.ClientHeight + GetBorderWidth;
|
|
CWidth := FGCache.ClientWidth + GetBorderWidth;
|
|
|
|
{$IFDEF dbgGridScroll}
|
|
DebugLn('aCol=%d aRow=%d FixHeight=%d CHeight=%d FixWidth=%d CWidth=%d',
|
|
[aCol,aRow,FGCache.FixedHeight,CHeight, FGCache.FixedWidth, CWidth]);
|
|
{$Endif}
|
|
|
|
while IsColumnIndexValid(fTopLeft.x) and
|
|
IsRowIndexValid(fTopLeft.y) do
|
|
begin
|
|
RNew:=CellRect(aCol,aRow);
|
|
if UseRightToLeftAlignment then begin
|
|
XInc := RNew.Right;
|
|
RNew.Right := FlipX(RNew.Left)+1;
|
|
RNew.Left := FlipX(XInc)+1;
|
|
end;
|
|
RNewStored := RNew;
|
|
|
|
Xinc := 0;
|
|
if RNew.Right <= FGCache.FixedWidth+GetBorderWidth then
|
|
Xinc := -1 // hidden at the left of fixedwidth line
|
|
else
|
|
if (RNew.Left > FGCache.FixedWidth+GetBorderWidth) and (RNew.Left >= CWidth) and not GetSmoothScroll(SB_Horz) then
|
|
Xinc := 1 // hidden at the right of clientwidth line
|
|
else
|
|
if (RNew.Left > FGCache.FixedWidth+GetBorderWidth) and
|
|
(CWidth < RNew.Right) and
|
|
(not (goDontScrollPartCell in Options) or ForceFullyVisible) then
|
|
begin // hidden / partially visible at the right
|
|
if not GetSmoothScroll(SB_Horz) then
|
|
Xinc := 1
|
|
else
|
|
begin
|
|
Inc(FGCache.TLColOff, RNew.Right-CWidth); // support smooth scroll
|
|
TLColOffChanged := True;
|
|
end;
|
|
end;
|
|
|
|
Yinc := 0;
|
|
if RNew.Bottom <= FGCache.FixedHeight+GetBorderWidth then
|
|
Yinc := -1 // hidden at the top of fixedheight line
|
|
else
|
|
if (RNew.Top > FGCache.FixedHeight+GetBorderWidth) and (RNew.Top >= CHeight) and not GetSmoothScroll(SB_Vert) then
|
|
YInc := 1 // hidden at the bottom of clientheight line
|
|
else
|
|
if (RNew.Top > FGCache.FixedHeight+GetBorderWidth) and
|
|
(CHeight < RNew.Bottom) and
|
|
(not (goDontScrollPartCell in Options) or ForceFullyVisible) then
|
|
begin // hidden / partially visible at bottom
|
|
if not GetSmoothScroll(SB_Vert) then
|
|
Yinc := 1
|
|
else
|
|
begin
|
|
Inc(FGCache.TLRowOff, RNew.Bottom-CHeight); // support smooth scroll
|
|
TLRowOffChanged := True;
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF dbgGridScroll}
|
|
with FTopLeft,RNew,FGCache do
|
|
DebugLn(' TL.C=%d TL.R=%d RNew:L=%d T=%d R=%d B=%d Xinc=%d YInc=%d ColOff=%d RowOff=%d',
|
|
[X,Y,Left,Top,Right,Bottom,XInc,YInc,TLColOff,TLRowOff]);
|
|
{$ENDIF}
|
|
|
|
if ((XInc=0)and(YInc=0)) or // the cell is already visible
|
|
((FTopLeft.X=aCol)and(FTopLeft.Y=aRow)) or // the cell is visible by definition
|
|
not IsColumnIndexValid(FTopLeft.X+XInc) or
|
|
not IsRowIndexValid(FTopLeft.Y+YInc)
|
|
then
|
|
Break;
|
|
Inc(FTopLeft.x, XInc);
|
|
if XInc<>0 then
|
|
FGCache.TLColOff := 0; // cancel col-offset for next calcs
|
|
Inc(FTopLeft.y, YInc);
|
|
if YInc<>0 then
|
|
FGCache.TLRowOff := 0; // cancel row-offset for next calcs
|
|
end;
|
|
|
|
// fix offsets
|
|
while (FTopLeft.x < ColCount-1) and (FGCache.TLColOff > ColWidths[FTopLeft.x]) do
|
|
begin
|
|
Dec(FGCache.TLColOff, ColWidths[FTopLeft.x]);
|
|
Inc(FTopLeft.x);
|
|
TLColOffChanged := True;
|
|
end;
|
|
while (FTopLeft.y < RowCount-1) and (FGCache.TLRowOff > RowHeights[FTopLeft.y]) do
|
|
begin
|
|
Dec(FGCache.TLRowOff, RowHeights[FTopLeft.y]);
|
|
Inc(FTopLeft.y);
|
|
TLRowOffChanged := True;
|
|
end;
|
|
|
|
Result := (OldTopLeft <> FTopLeft) or TLColOffChanged or TLRowOffChanged;
|
|
|
|
BeginUpdate;
|
|
try
|
|
if Result then begin
|
|
if (OldTopLeft <> FTopLeft) then
|
|
doTopleftChange(False)
|
|
else
|
|
VisualChange;
|
|
end;
|
|
if not (goDontScrollPartCell in Options) or ForceFullyVisible then
|
|
begin
|
|
RNew := RNewStored;
|
|
if ResetOffset(
|
|
not GetSmoothScroll(SB_Horz) or
|
|
(RNew.Left < FGCache.FixedWidth+GetBorderWidth), // partially visible on left
|
|
(not GetSmoothScroll(SB_Vert) or
|
|
(RNew.Top < FGCache.FixedHeight+GetBorderWidth))) // partially visible on top
|
|
then
|
|
Result := True;
|
|
end;
|
|
finally
|
|
EndUpdate(Result);
|
|
end;
|
|
end;
|
|
|
|
{Returns a valid TopLeft from a proposed TopLeft[DCol,DRow] which are
|
|
relative or absolute coordinates }
|
|
function TCustomGrid.ScrollGrid(Relative: Boolean; DCol, DRow: Integer): TPoint;
|
|
begin
|
|
Result:=FTopLeft;
|
|
if not Relative then begin
|
|
DCol:=DCol-Result.x;
|
|
DRow:=DRow-Result.y;
|
|
end;
|
|
|
|
if DCol<>0 then begin
|
|
if DCol+Result.x<FFixedCols then DCol:=Result.x-FFixedCols else
|
|
if DCol+Result.x>ColCount-1 then DCol:=ColCount-1-Result.x;
|
|
end;
|
|
if DRow<>0 then begin
|
|
if DRow+Result.y<FFixedRows then DRow:=Result.y-FFixedRows else
|
|
if DRow+Result.y>RowCount-1 then DRow:=RowCount-1-Result.y;
|
|
end;
|
|
|
|
Inc(Result.x, DCol);
|
|
Inc(Result.y, DRow);
|
|
|
|
Result.x := Max(FixedCols, Min(Result.x, FGCache.MaxTopLeft.x));
|
|
Result.y := Max(FixedRows, Min(Result.y, FGCache.MaxTopLeft.y));
|
|
end;
|
|
|
|
procedure TCustomGrid.TopLeftChanged;
|
|
begin
|
|
if Assigned(OnTopLeftChanged) and not (csDesigning in ComponentState) then
|
|
OnTopLeftChanged(Self);
|
|
end;
|
|
|
|
procedure TCustomGrid.HeaderClick(IsColumn: Boolean; index: Integer);
|
|
var
|
|
ColOfs: Integer;
|
|
begin
|
|
if IsColumn and FColumnClickSorts then begin
|
|
// Determine the sort order.
|
|
if index = FSortColumn then begin
|
|
case FSortOrder of // Same column clicked again -> invert the order.
|
|
soAscending: FSortOrder:=soDescending;
|
|
soDescending: FSortOrder:=soAscending;
|
|
end;
|
|
end
|
|
else
|
|
FSortOrder := soAscending; // Ascending order to start with.
|
|
|
|
FSortColumn := index;
|
|
Sort(True, index, FFixedRows, RowCount-1);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.HeaderSized(IsColumn: Boolean; index: Integer);
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomGrid.ColRowMoved(IsColumn: Boolean; FromIndex,ToIndex: Integer);
|
|
begin
|
|
end;
|
|
|
|
// Notification to inform derived grids to exchange their actual rows data
|
|
procedure TCustomGrid.ColRowExchanged(IsColumn: Boolean; index, WithIndex: Integer);
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomGrid.ColRowInserted(IsColumn: boolean; index: integer);
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawFocusRect(aCol, aRow: Integer; ARect: TRect);
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomGrid.AutoAdjustColumn(aCol: Integer);
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomGrid.SizeChanged(OldColCount, OldRowCount: Integer);
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomGrid.ColRowDeleted(IsColumn: Boolean; index: Integer);
|
|
begin
|
|
end;
|
|
|
|
function TCustomGrid.CanEditShow: Boolean;
|
|
begin
|
|
Result := EditingAllowed(FCol) and not (csDesigning in ComponentState)
|
|
and CanFocus;
|
|
end;
|
|
|
|
procedure TCustomGrid.Paint;
|
|
{$ifdef DbgPaint}
|
|
var
|
|
R: TRect;
|
|
{$endif}
|
|
begin
|
|
//
|
|
{$ifdef DbgPaint}
|
|
R := Canvas.ClipRect;
|
|
DebugLn('TCustomGrid.Paint %s Row=%d Clip=%s',[DbgSName(Self),Row,Dbgs(R)]);
|
|
{$endif}
|
|
if ([gfVisualChange,gfClientRectChange]*fGridFlags<>[]) or
|
|
(ClientWidth<>FGCache.ClientWidth) or
|
|
(ClientHeight<>FGCache.ClientHeight) then begin
|
|
{$ifdef DbgVisualChange}
|
|
DebugLnEnter('Resetting Sizes in Paint INIT');
|
|
{$endif}
|
|
FGridFlags := FGridFlags + [gfPainting];
|
|
ResetSizes;
|
|
FGridFlags := FGridFlags - [gfVisualChange, gfPainting, gfClientRectChange];
|
|
{$ifdef DbgVisualChange}
|
|
DebugLnExit('Resetting Sizes in Paint DONE');
|
|
{$endif}
|
|
end;
|
|
inherited Paint;
|
|
if FUpdateCount=0 then begin
|
|
DrawEdges;
|
|
DrawAllRows;
|
|
DrawColRowMoving;
|
|
DrawBorder;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.PickListItemSelected(Sender: TObject);
|
|
begin
|
|
if Assigned(OnPickListSelect) then
|
|
OnPickListSelect(Self);
|
|
end;
|
|
|
|
procedure TCustomGrid.PrepareCanvas(aCol, aRow: Integer; aState: TGridDrawState);
|
|
function GetNotSelectedColor: TColor;
|
|
begin
|
|
Result := GetColumnColor(aCol, gdFixed in AState);
|
|
if (gdFixed in AState) and (gdHot in aState) then
|
|
Result := FFixedHotColor;
|
|
if not (gdFixed in AState) and (FAlternateColor<>Result) then begin
|
|
if Result=Color then begin
|
|
// column color = grid Color, Allow override color
|
|
// 1. default color after fixed rows
|
|
// 2. always use absolute alternate color based in odd & even row
|
|
if (FAltColorStartNormal and Odd(ARow-FixedRows)) {(1)} or
|
|
(not FAltColorStartNormal and Odd(ARow)) {(2)} then
|
|
Result := FAlternateColor;
|
|
end;
|
|
end;
|
|
if (gdRowHighlight in aState) and not (gdFixed in AState) then
|
|
Result := ColorToRGB(Result) xor $1F1F1F
|
|
end;
|
|
function SimpleColorDistance(C1, C2: TColor): Double;
|
|
var
|
|
r1, g1, b1, r2, g2, b2: Byte;
|
|
begin
|
|
RedGreenBlue(C1, r1, g1, b1);
|
|
RedGreenBlue(C2, r2, g2, b2);
|
|
Result := Sqrt(Sqr(Integer(r1) - r2) + Sqr(Integer(g1) - g2) + Sqr(Integer(b1) - b2));
|
|
end;
|
|
var
|
|
C, C1, FontColor: TColor;
|
|
CurrentTextStyle: TTextStyle;
|
|
IsSelected: boolean;
|
|
gc: TGridColumn;
|
|
begin
|
|
if (gdFixed in aState) or DefaultDrawing then begin
|
|
Canvas.Pen.Mode := pmCopy;
|
|
GetSelectedState(aState, IsSelected);
|
|
if IsSelected then begin
|
|
FontColor:=clHighlightText;
|
|
if FEditorMode and (aCol = Self.Col)
|
|
and (((FEditor=FStringEditor) and (FStringEditor.BorderStyle=bsNone))
|
|
or (FEditor=FButtonStringEditor))
|
|
then
|
|
Canvas.Brush.Color := FEditor.Color
|
|
else if FEditorMode and (aCol = Self.Col) and (FEditor=FPicklistEditor) then
|
|
Canvas.Brush.Color := GetNotSelectedColor
|
|
else if FadeUnfocusedSelection and not Focused then begin
|
|
C := ColorToRGB(Color);
|
|
C1 := ColorToRGB(clBtnFace);
|
|
if SimpleColorDistance(C, C1) >= 25 then begin // Windows: clWindow = FFFFFF, clBtnFace = F0F0F0
|
|
Canvas.Brush.Color := clBtnFace;
|
|
FontColor := clBtnText;
|
|
end else begin
|
|
Canvas.Brush.Color := clInactiveCaption;
|
|
FontColor := clInactiveCaptionText;
|
|
end;
|
|
end
|
|
else
|
|
Canvas.Brush.Color := SelectedColor;
|
|
SetCanvasFont(GetColumnFont(aCol, False));
|
|
if not IsCellButtonColumn(point(aCol,aRow)) then
|
|
Canvas.Font.Color := FontColor;
|
|
FLastFont:=nil;
|
|
end else begin
|
|
Canvas.Brush.Color := GetNotSelectedColor;
|
|
SetCanvasFont(GetColumnFont(aCol, ((gdFixed in aState) and (aRow < FFixedRows))));
|
|
end;
|
|
if not Enabled and (FDisabledFontColor<>clNone) then
|
|
Canvas.Font.Color := FDisabledFontColor;
|
|
CurrentTextStyle := DefaultTextStyle;
|
|
CurrentTextStyle.Alignment := BidiFlipAlignment(GetColumnAlignment(aCol, gdFixed in AState), UseRightToLeftAlignment);
|
|
CurrentTextStyle.Layout := GetColumnLayout(aCol, gdFixed in AState);
|
|
CurrentTextStyle.ShowPrefix := ((gdFixed in aState) and (aRow < FFixedRows)) and GetTitleShowPrefix(aCol);
|
|
CurrentTextStyle.RightToLeft := UseRightToLeftReading;
|
|
CurrentTextStyle.EndEllipsis := (goCellEllipsis in Options);
|
|
gc := ColumnFromGridColumn(aCol);
|
|
CurrentTextStyle.SingleLine := (gc = nil) or (not gc.Title.MultiLine);
|
|
Canvas.TextStyle := CurrentTextStyle;
|
|
end else begin
|
|
CurrentTextStyle := DefaultTextStyle;
|
|
CurrentTextStyle.Alignment := BidiFlipAlignment(CurrentTextStyle.Alignment, UseRightToLeftAlignment);
|
|
CurrentTextStyle.RightToLeft := UseRightToLeftAlignment;
|
|
Canvas.TextStyle := CurrentTextStyle;
|
|
Canvas.Brush.Color := clWindow;
|
|
Canvas.Font.Color := clWindowText;
|
|
end;
|
|
|
|
DoPrepareCanvas(aCol, aRow, aState);
|
|
end;
|
|
|
|
procedure TCustomGrid.PrepareCellHints(ACol, ARow: Integer);
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomGrid.ResetDefaultColWidths;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if not AutoFillColumns then begin
|
|
for i:=0 to ColCount-1 do
|
|
FCols[i] := -1;
|
|
VisualChange;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.UnprepareCellHints;
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomGrid.ResetEditor;
|
|
begin
|
|
EditorGetValue(True);
|
|
if EditorAlwaysShown then
|
|
EditorShow(True);
|
|
end;
|
|
|
|
// Reset the last Row or Col movement
|
|
procedure TCustomGrid.ResetLastMove;
|
|
begin
|
|
FMoveLast:=Point(-1,-1);
|
|
end;
|
|
|
|
procedure TCustomGrid.ResetHotCell;
|
|
begin
|
|
with FGCache do begin
|
|
if HotCellPainted and IsColumnIndexValid(HotCell.x) and IsRowIndexValid(HotCell.y) then
|
|
InvalidateCell(HotCell.X, HotCell.Y);
|
|
HotCell := Point(-1,-1);
|
|
HotCellPainted := False;
|
|
HotGridZone := gzInvalid;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.ResetPushedCell(ResetColRow: boolean=True);
|
|
begin
|
|
with FGCache do begin
|
|
if ClickCellPushed then
|
|
InvalidateCell(PushedCell.X, PushedCell.Y);
|
|
if ResetColRow then
|
|
PushedCell := Point(-1,-1);
|
|
ClickCellPushed := False;
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.ResetOffset(chkCol, ChkRow: Boolean): Boolean;
|
|
begin
|
|
if ChkCol then ChkCol:=FGCache.TLColOff<>0;
|
|
if ChkCol then FGCache.TlColOff:=0;
|
|
if ChkRow then ChkRow:=FGCache.TLRowOff<>0;
|
|
if ChkRow then FGCache.TlRowOff:=0;
|
|
Result := ChkRow or ChkCol;
|
|
if Result then
|
|
begin
|
|
CacheVisibleGrid;
|
|
VisualChange;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.ResizeColumn(aCol, aWidth: Integer);
|
|
begin
|
|
if aWidth<0 then
|
|
aWidth:=0;
|
|
ColWidths[aCol] := aWidth;
|
|
end;
|
|
|
|
procedure TCustomGrid.ResizeRow(aRow, aHeight: Integer);
|
|
begin
|
|
if aHeight<0 then
|
|
aHeight:=0;
|
|
RowHeights[aRow] := aHeight;
|
|
end;
|
|
|
|
procedure TCustomGrid.HeaderSizing(const IsColumn: boolean; const AIndex,
|
|
ASize: Integer);
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomGrid.ShowCellHintWindow(APoint: TPoint);
|
|
var
|
|
cell: TPoint;
|
|
txt1, txt2, txt, AppHint: String;
|
|
w: Integer;
|
|
gds: TGridDrawState;
|
|
|
|
procedure AddToHint(var AHint: String; const ANew: String);
|
|
begin
|
|
if ANew = '' then
|
|
exit;
|
|
if AHint = '' then AHint := ANew else AHint := AHint + LineEnding + ANew;
|
|
end;
|
|
|
|
begin
|
|
if not ShowHint then
|
|
exit;
|
|
|
|
cell := MouseToCell(APoint);
|
|
if (cell.x = -1) or (cell.y = -1) then
|
|
exit;
|
|
|
|
txt1 := ''; // Hint returned by OnGetCellHint
|
|
txt2 := ''; // Hint returned by GetTruncCellHintText
|
|
AppHint := ''; // Hint to be displayed in Statusbar
|
|
txt := ''; // Hint to be displayed as popup
|
|
PrepareCellHints(cell.x, cell.y); // in DBGrid, set the active record to cell.y
|
|
try
|
|
if (goCellHints in Options) and (FCellHintPriority <> chpTruncOnly) then
|
|
txt1 := GetCellHintText(cell.x, cell.y);
|
|
if (goTruncCellHints in Options) then begin
|
|
txt2 := GetTruncCellHintText(cell.x, cell.y);
|
|
gds := GetGridDrawState(cell.x, cell.y);
|
|
PrepareCanvas(cell.x, cell.y, gds);
|
|
w := Canvas.TextWidth(txt2) + varCellPadding*2;
|
|
if w < ColWidths[cell.x] then
|
|
txt2 := '';
|
|
end;
|
|
finally
|
|
UnprepareCellHints;
|
|
end;
|
|
|
|
case FCellHintPriority of
|
|
chpAll:
|
|
begin
|
|
AddToHint(txt, GetShortHint(FSavedHint));
|
|
AddToHint(txt, GetShortHint(txt1));
|
|
AddToHint(txt, txt2);
|
|
AddToHint(AppHint, GetLongHint(FSavedHint));
|
|
AddToHint(AppHint, GetLongHint(txt1));
|
|
end;
|
|
chpAllNoDefault:
|
|
begin
|
|
AddToHint(txt, GetShortHint(txt1));
|
|
AddToHint(txt, txt2);
|
|
AddToHint(AppHint, GetLongHint(txt1));
|
|
end;
|
|
chpTruncOnly:
|
|
begin
|
|
AddToHint(txt, txt2);
|
|
AppHint := txt;
|
|
if Pos('|', AppHint) = 0 then
|
|
AppHint := AppHint + '|';
|
|
end;
|
|
end;
|
|
|
|
(*
|
|
if (txt = '') and (FSavedHint <> '') then
|
|
txt := FSavedHint;
|
|
if (AppHint = '') then AppHint := FSavedhint;
|
|
*)
|
|
|
|
if not EditorMode and not (csDesigning in ComponentState) then begin
|
|
Hint := txt;
|
|
//set Application.Hint as well (issue #0026957)
|
|
Application.Hint := GetLongHint(AppHint);
|
|
Application.ActivateHint(APoint, true);
|
|
end else
|
|
HideCellHintWindow;
|
|
end;
|
|
|
|
procedure TCustomGrid.HideCellHintWindow;
|
|
begin
|
|
Hint := FSavedHint;
|
|
Application.CancelHint;
|
|
end;
|
|
|
|
procedure TCustomGrid.StartPushCell;
|
|
begin
|
|
fGridState := gsButtonColumnClicking;
|
|
DoPushCell;
|
|
end;
|
|
|
|
function TCustomGrid.TitleFontIsStored: Boolean;
|
|
begin
|
|
Result := not FTitleFontIsDefault;
|
|
end;
|
|
|
|
function TCustomGrid.SelectCell(ACol, ARow: Integer): Boolean;
|
|
begin
|
|
Result := (ColWidths[aCol] > 0) and (RowHeights[aRow] > 0);
|
|
end;
|
|
|
|
procedure TCustomGrid.SetCanvasFont(aFont: TFont);
|
|
begin
|
|
if (aFont<>FLastFont) or
|
|
not Canvas.Font.IsEqual(aFont) then
|
|
begin
|
|
Canvas.Font := aFont;
|
|
FLastFont := AFont;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetColor(Value: TColor);
|
|
begin
|
|
if AlternateColor = Color then
|
|
FAlternateColor := Value;
|
|
inherited SetColor(Value);
|
|
end;
|
|
|
|
procedure TCustomGrid.SetColRow(const ACol, ARow: Integer; withEvents: boolean);
|
|
begin
|
|
if withEvents then begin
|
|
MoveExtend(false, aCol, aRow, true);
|
|
Click;
|
|
end else begin
|
|
FCol := ACol;
|
|
FRow := ARow;
|
|
UpdateSelectionRange;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetCursor(AValue: TCursor);
|
|
begin
|
|
inherited;
|
|
ChangeCursor(AValue);
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawBorder;
|
|
var
|
|
R: TRect;
|
|
begin
|
|
if InternalNeedBorder then begin
|
|
R := Rect(0,0,ClientWidth-1, Clientheight-1);
|
|
// The following line is a simple workaround for a more complex problem
|
|
// caused by Canvas.SaveHandleState and Canvas.RestoreHandleState in DoDrawCell
|
|
// see the notes in the related bug report #34890
|
|
Canvas.Pen.Color := fBorderColor + 1;
|
|
Canvas.Pen.Color := fBorderColor;
|
|
Canvas.Pen.Width := 1;
|
|
Canvas.MoveTo(0,0);
|
|
Canvas.LineTo(0,R.Bottom);
|
|
Canvas.LineTo(R.Right, R.Bottom);
|
|
Canvas.LineTo(R.Right, 0);
|
|
Canvas.LineTo(0,0);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawColRowMoving;
|
|
{$ifdef AlternativeMoveIndicator}
|
|
var
|
|
x, y, dx, dy: Integer;
|
|
R: TRect;
|
|
{$endif}
|
|
begin
|
|
if (FGridState=gsColMoving)and(fMoveLast.x>=0) then begin
|
|
{$ifdef AlternativeMoveIndicator}
|
|
dx := 4;
|
|
dy := 4;
|
|
Canvas.pen.Width := 1;
|
|
Canvas.Pen.Color := clBlack;
|
|
Canvas.Brush.Color := clWhite;
|
|
R := CellRect(FMoveLast.X, 0);
|
|
Y := R.Top + (R.Bottom-R.Top) div 2;
|
|
X := R.Left - 2*dx;
|
|
Canvas.Polygon([Point(x,y+dy),point(x,y-dy),point(x+dx,y), point(x,y+dy)]);
|
|
X := R.Left + 2*dx;
|
|
Canvas.Polygon([Point(x,y+dy),point(x,y-dy),point(x-dx,y), point(x,y+dy)]);
|
|
{$else}
|
|
Canvas.Pen.Width:=3;
|
|
Canvas.Pen.Color:=FColRowDragIndicatorColor;
|
|
Canvas.MoveTo(fMoveLast.y, 0);
|
|
Canvas.Lineto(fMovelast.y, FGCache.MaxClientXY.Y);
|
|
Canvas.Pen.Width:=1;
|
|
{$endif}
|
|
end else
|
|
if (FGridState=gsRowMoving)and(FMoveLast.y>=0) then begin
|
|
{$ifdef AlternativeMoveIndicator}
|
|
dx := 4;
|
|
dy := 4;
|
|
Canvas.pen.Width := 1;
|
|
Canvas.Pen.Color := clBlack;
|
|
Canvas.Brush.Color := clWhite;
|
|
R := CellRect(0, FMoveLast.Y);
|
|
X := R.Left + (R.Right-R.Left) div 2;
|
|
Y := R.Top - 2*dy;
|
|
Canvas.Polygon([Point(x-dx,y),point(x+dx,y),point(x,y+dy), point(x-dx,y)]);
|
|
Y := R.Top + 2*dy;
|
|
Canvas.Polygon([Point(x-dx,y),point(x+dx,y),point(x,y-dy), point(x-dx,y)]);
|
|
{$else}
|
|
Canvas.Pen.Width:=3;
|
|
Canvas.Pen.Color:=FColRowDragIndicatorColor;
|
|
if UseRightToLeftAlignment then begin
|
|
Canvas.MoveTo(FGCache.ClientRect.Right, FMoveLast.X);
|
|
Canvas.LineTo(FlipX(FGCache.MaxClientXY.X), FMoveLast.X);
|
|
end
|
|
else begin
|
|
Canvas.MoveTo(0, FMoveLast.X);
|
|
Canvas.LineTo(FGCache.MaxClientXY.X, FMoveLast.X);
|
|
end;
|
|
Canvas.Pen.Width:=1;
|
|
{$endif}
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawColumnText(aCol, aRow: Integer; aRect: TRect;
|
|
aState: TGridDrawState);
|
|
begin
|
|
DrawColumnTitleImage(aRect, aCol);
|
|
DrawCellText(aCol,aRow,aRect,aState,GetColumnTitle(aCol))
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawColumnTitleImage(
|
|
var ARect: TRect; AColumnIndex: Integer);
|
|
var
|
|
w, h, rw, rh, ImgIndex, ImgListWidth: Integer;
|
|
p: TPoint;
|
|
r: TRect;
|
|
ImgLayout: TButtonLayout;
|
|
ImgList: TCustomImageList;
|
|
ImgRes: TScaledImageListResolution;
|
|
s: TSize;
|
|
Details: TThemedElementDetails;
|
|
NativeSortGlyphs: Boolean;
|
|
begin
|
|
if FSortColumn = AColumnIndex then
|
|
begin
|
|
GetSortTitleImageInfo(AColumnIndex, ImgList, ImgIndex, ImgListWidth, NativeSortGlyphs);
|
|
if NativeSortGlyphs then// draw native sort buttons
|
|
begin
|
|
case FSortOrder of
|
|
soAscending: Details := ThemeServices.GetElementDetails(thHeaderSortArrowSortedUp);
|
|
soDescending: Details := ThemeServices.GetElementDetails(thHeaderSortArrowSortedDown);
|
|
end;
|
|
// Maybe: s := ThemeServices.GetDetailSizeForPPI(Details, Font.PixelsPerInch);
|
|
s := ThemeServices.GetDetailSize(Details);
|
|
end else
|
|
s := Size(-1, -1);
|
|
if s.cx>0 then // theme services support sorted arrows
|
|
begin
|
|
w := Scale96ToFont(s.cx);
|
|
h := Scale96ToFont(s.cy);
|
|
|
|
if IsRightToLeft then begin
|
|
r.Left := ARect.Left + DEFIMAGEPADDING;
|
|
Inc(ARect.Left, w + DEFIMAGEPADDING);
|
|
end else begin
|
|
Dec(ARect.Right, w + DEFIMAGEPADDING);
|
|
r.Left := ARect.Right - DEFIMAGEPADDING;
|
|
end;
|
|
r.Right := r.Left + w;
|
|
r.Top := ARect.Top + (ARect.Bottom - ARect.Top - h) div 2;
|
|
r.Bottom := r.Top + h;
|
|
|
|
ThemeServices.DrawElement(Canvas.Handle, Details, r, nil);
|
|
end else
|
|
begin
|
|
ImgRes := ImgList.ResolutionForPPI[ImgListWidth, Font.PixelsPerInch, GetCanvasScaleFactor];
|
|
w := ImgRes.Width;
|
|
h := ImgRes.Height;
|
|
|
|
if IsRightToLeft then begin
|
|
P.X := ARect.Left + DEFIMAGEPADDING;
|
|
Inc(ARect.Left, w + DEFIMAGEPADDING);
|
|
end else begin
|
|
Dec(ARect.Right, w + DEFIMAGEPADDING);
|
|
p.X := ARect.Right - DEFIMAGEPADDING;
|
|
end;
|
|
p.Y := ARect.Top + (ARect.Bottom - ARect.Top - h) div 2;
|
|
|
|
ImgRes.Draw(Canvas, p.X, p.Y, ImgIndex);
|
|
end;
|
|
end;
|
|
|
|
if FTitleImageList<>nil then
|
|
begin
|
|
GetTitleImageInfo(AColumnIndex, ImgIndex, ImgLayout);
|
|
if ImgIndex>=0 then
|
|
begin
|
|
ImgRes := FTitleImageList.ResolutionForPPI[FTitleImageListWidth, Font.PixelsPerInch, GetCanvasScaleFactor];
|
|
w := ImgRes.Width;
|
|
h := ImgRes.Height;
|
|
rw := ARect.Right - ARect.Left - DEFIMAGEPADDING * 2;
|
|
rh := ARect.Bottom - ARect.Top - DEFIMAGEPADDING * 2;
|
|
|
|
case ImgLayout of
|
|
blGlyphRight, blGlyphLeft:
|
|
p.Y := ARect.Top + (rh - h) div 2 + DEFIMAGEPADDING;
|
|
blGlyphTop, blGlyphBottom:
|
|
p.X := ARect.Left + (rw - w) div 2 + DEFIMAGEPADDING;
|
|
end;
|
|
case ImgLayout of
|
|
blGlyphRight: begin
|
|
Dec(ARect.Right, w + DEFIMAGEPADDING * 2);
|
|
p.X := ARect.Right + DEFIMAGEPADDING;
|
|
end;
|
|
blGlyphLeft: begin
|
|
p.X := ARect.Left + DEFIMAGEPADDING;
|
|
Inc(ARect.Left, w + DEFIMAGEPADDING * 2);
|
|
end;
|
|
blGlyphTop: begin
|
|
p.Y := ARect.Top + DEFIMAGEPADDING;
|
|
Inc(ARect.Top, w + DEFIMAGEPADDING * 2);
|
|
end;
|
|
blGlyphBottom: begin
|
|
Dec(ARect.Bottom, w + DEFIMAGEPADDING * 2);
|
|
p.Y := ARect.Bottom + DEFIMAGEPADDING;
|
|
end;
|
|
end;
|
|
|
|
ImgRes.Draw(Canvas, p.X, p.Y, ImgIndex);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawCell(aCol, aRow: Integer; aRect: TRect;
|
|
aState: TGridDrawState);
|
|
begin
|
|
PrepareCanvas(aCol, aRow, aState);
|
|
DrawFillRect(Canvas, aRect);
|
|
DrawCellGrid(aCol,aRow,aRect,aState);
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawAllRows;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
// Draw Rows
|
|
with FGCache.VisibleGrid do
|
|
for i:=Top to Bottom do
|
|
DrawRow(i);
|
|
// Draw Fixed Rows
|
|
for i:=0 to FFixedRows-1 do
|
|
DrawRow(i);
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawFillRect(aCanvas: TCanvas; R: TRect);
|
|
begin
|
|
if UseRightToLeftAlignment then
|
|
OffsetRect(R, 1, 0);
|
|
aCanvas.FillRect(R);
|
|
end;
|
|
|
|
function VerticalIntersect(const aRect,bRect: TRect): boolean;
|
|
begin
|
|
result := (aRect.Top < bRect.Bottom) and (aRect.Bottom > bRect.Top);
|
|
end;
|
|
|
|
function HorizontalIntersect(const aRect,bRect: TRect): boolean;
|
|
begin
|
|
result := (aRect.Left < bRect.Right) and (aRect.Right > bRect.Left);
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawRow(aRow: Integer);
|
|
var
|
|
gds: TGridDrawState;
|
|
aCol, exCol, orgTop, orgBottom: Integer;
|
|
Rs, colSpanning: Boolean;
|
|
R: TRect;
|
|
ClipArea: Trect;
|
|
|
|
procedure DoDrawCell;
|
|
begin
|
|
with FGCache do begin
|
|
if (aCol=HotCell.x) and (aRow=HotCell.y) and not IsPushCellActive() then begin
|
|
Include(gds, gdHot);
|
|
HotCellPainted := True;
|
|
end;
|
|
if ClickCellPushed and (aCol=PushedCell.x) and (aRow=PushedCell.y) then begin
|
|
Include(gds, gdPushed);
|
|
end;
|
|
end;
|
|
|
|
Canvas.SaveHandleState;
|
|
try
|
|
InterSectClipRect(Canvas.Handle, R.Left, R.Top, R.Right, R.Bottom);
|
|
DrawCell(aCol, aRow, R, gds);
|
|
finally
|
|
Canvas.RestoreHandleState;
|
|
end;
|
|
end;
|
|
begin
|
|
|
|
// Upper and Lower bounds for this row
|
|
ColRowToOffSet(False, True, aRow, R.Top, R.Bottom);
|
|
orgTop := R.Top;
|
|
orgBottom := R.Bottom;
|
|
// is this row within the ClipRect?
|
|
ClipArea := Canvas.ClipRect;
|
|
if (R.Top>=R.Bottom) or not VerticalIntersect(R, ClipArea) then begin
|
|
{$IFDEF DbgVisualChange}
|
|
DebugLn('Drawrow: Skipped row: ', IntToStr(aRow));
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
colSpanning := (goColSpanning in Options);
|
|
|
|
// Draw columns in this row
|
|
with FGCache.VisibleGrid do begin
|
|
|
|
aCol := left;
|
|
while aCol<=Right do begin
|
|
ColRowToOffset(True, True, aCol, R.Left, R.Right);
|
|
if (R.Left<R.Right) and HorizontalIntersect(R, ClipArea) then begin
|
|
|
|
if colSpanning then
|
|
CellExtent(aCol, aRow, R, exCol);
|
|
|
|
gds := GetGridDrawState(ACol, ARow);
|
|
DoDrawCell;
|
|
|
|
if colSpanning then begin
|
|
aCol := exCol;
|
|
R.Top := orgTop;
|
|
R.Bottom := orgBottom;
|
|
end;
|
|
end;
|
|
inc(aCol);
|
|
end;
|
|
|
|
Rs := (goRowSelect in Options);
|
|
// Draw the focus Rect
|
|
if FFocusRectVisible and (ARow=FRow) and
|
|
((Rs and (ARow>=Top) and (ARow<=Bottom)) or IsCellVisible(FCol,ARow))
|
|
then begin
|
|
if EditorMode then begin
|
|
//if EditorAlwaysShown and (FEditor<>nil) and FEditor.Visible then begin
|
|
//DebugLn('No Draw Focus Rect');
|
|
end else begin
|
|
if Rs then
|
|
CalcFocusRect(R, false) // will be adjusted when calling DrawFocusRect
|
|
else begin
|
|
ColRowToOffset(True, True, FCol, R.Left, R.Right);
|
|
if colSpanning then
|
|
CellExtent(FCol, aRow, R, exCol);
|
|
end;
|
|
// is this column within the ClipRect?
|
|
if HorizontalIntersect(R, ClipArea) then
|
|
DrawFocusRect(FCol,FRow, R);
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
// Draw Fixed Columns
|
|
aCol := 0;
|
|
while aCol<=FFixedCols-1 do begin
|
|
gds:=[gdFixed];
|
|
ColRowToOffset(True, True, aCol, R.Left, R.Right);
|
|
// is this column within the ClipRect?
|
|
if (R.Left<R.Right) and HorizontalIntersect(R, ClipArea) then begin
|
|
if colSpanning then
|
|
CellExtent(aCol, aRow, R, exCol);
|
|
DoDrawCell;
|
|
if colSpanning then begin
|
|
aCol := exCol;
|
|
R.Top := orgTop;
|
|
R.Bottom := orgBottom;
|
|
end;
|
|
end;
|
|
inc(aCol);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditButtonClicked(Sender: TObject);
|
|
begin
|
|
if Assigned(OnEditButtonClick) or Assigned(OnButtonClick) then begin
|
|
if Sender=FButtonEditor then
|
|
DoEditButtonClick(FButtonEditor.Col, FButtonEditor.Row)
|
|
else
|
|
DoEditButtonClick(FCol, FRow);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawEdges;
|
|
var
|
|
P: TPoint;
|
|
Cr: TRect;
|
|
begin
|
|
P:=FGCache.MaxClientXY;
|
|
Cr:=Bounds(0,0, FGCache.ClientWidth, FGCache.ClientHeight);
|
|
if P.x<Cr.Right then begin
|
|
if UseRightToLeftAlignment then
|
|
Cr.Right:=Cr.Right - P.x
|
|
else
|
|
Cr.Left:=P.x;
|
|
Canvas.Brush.Color:= Color;
|
|
Canvas.FillRect(cr);
|
|
if UseRightToLeftAlignment then begin
|
|
Cr.Left := Cr.Right;
|
|
Cr.Right:=FGCache.ClientWidth;
|
|
end
|
|
else begin
|
|
Cr.Right:=Cr.Left;
|
|
Cr.Left:=0;
|
|
end;
|
|
end;
|
|
if P.y<Cr.Bottom then begin
|
|
Cr.Top:=p.y;
|
|
Canvas.Brush.Color:= Color;
|
|
Canvas.FillRect(cr);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawCellGrid(aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState);
|
|
var
|
|
dv,dh: Boolean;
|
|
OldCosmeticUsed, OldCosmetic: Boolean;
|
|
begin
|
|
OldCosmeticUsed := false;
|
|
|
|
with Canvas do begin
|
|
|
|
// fixed cells
|
|
if (gdFixed in aState) then begin
|
|
Dv := goFixedVertLine in Options;
|
|
Dh := goFixedHorzLine in Options;
|
|
Pen.Style := psSolid;
|
|
if FGridLineWidth > 0 then
|
|
Pen.Width := 1
|
|
else
|
|
Pen.Width := 0;
|
|
if not FFlat then begin
|
|
if FTitleStyle=tsNative then
|
|
exit
|
|
else
|
|
if FGridLineWidth > 0 then begin
|
|
if gdPushed in aState then
|
|
Pen.Color := cl3DShadow
|
|
else
|
|
Pen.Color := cl3DHilight;
|
|
if UseRightToLeftAlignment then begin
|
|
//the light still on the left but need to new x
|
|
MoveTo(aRect.Right, aRect.Top);
|
|
LineTo(aRect.Left + 1, aRect.Top);
|
|
LineTo(aRect.Left + 1, aRect.Bottom);
|
|
end else begin
|
|
MoveTo(aRect.Right - 1, aRect.Top);
|
|
LineTo(aRect.Left, aRect.Top);
|
|
LineTo(aRect.Left, aRect.Bottom);
|
|
end;
|
|
if FTitleStyle=tsStandard then begin
|
|
// more contrast
|
|
if gdPushed in aState then
|
|
Pen.Color := cl3DHilight
|
|
else
|
|
Pen.Color := cl3DShadow;
|
|
if UseRightToLeftAlignment then begin
|
|
MoveTo(aRect.Left+2, aRect.Bottom-2);
|
|
LineTo(aRect.Right, aRect.Bottom-2);
|
|
LineTo(aRect.Right, aRect.Top);
|
|
end else begin
|
|
MoveTo(aRect.Left+1, aRect.Bottom-2);
|
|
LineTo(aRect.Right-2, aRect.Bottom-2);
|
|
LineTo(aRect.Right-2, aRect.Top);
|
|
end;
|
|
end;
|
|
end;
|
|
Pen.Color := cl3DDKShadow;
|
|
end else begin
|
|
Pen.Color := FFixedGridLineColor;
|
|
end;
|
|
end else begin
|
|
Dv := goVertLine in Options;
|
|
Dh := goHorzLine in Options;
|
|
OldCosmeticUsed := true;
|
|
OldCosmetic := Pen.Cosmetic;
|
|
Pen.Cosmetic := false;
|
|
Pen.Style := fGridLineStyle;
|
|
Pen.Color := fGridLineColor;
|
|
Pen.Width := fGridLineWidth;
|
|
end;
|
|
|
|
// non-fixed cells
|
|
if fGridLineWidth > 0 then begin
|
|
if Dh then begin
|
|
MoveTo(aRect.Left, aRect.Bottom - 1);
|
|
LineTo(aRect.Right, aRect.Bottom - 1);
|
|
end;
|
|
if Dv then begin
|
|
if UseRightToLeftAlignment then begin
|
|
MoveTo(aRect.Left, aRect.Top);
|
|
LineTo(aRect.Left, aRect.Bottom);
|
|
end else begin
|
|
MoveTo(aRect.Right - 1, aRect.Top);
|
|
LineTo(aRect.Right - 1, aRect.Bottom);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if OldCosmeticUsed then
|
|
Pen.Cosmetic := OldCosmetic;
|
|
end; // with canvas,rect
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawTextInCell(aCol, aRow: Integer; aRect: TRect;
|
|
aState: TGridDrawState);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawThemedCell(aCol, aRow: Integer; aRect: TRect;
|
|
aState: TGridDrawState);
|
|
var
|
|
details: TThemedElementDetails;
|
|
begin
|
|
if gdPushed in aState then
|
|
Details := ThemeServices.GetElementDetails(thHeaderItemPressed)
|
|
else
|
|
if gdHot in aState then
|
|
Details := ThemeServices.GetElementDetails(thHeaderItemHot)
|
|
else
|
|
Details := ThemeServices.GetElementDetails(thHeaderItemNormal);
|
|
ThemeSErvices.DrawElement(Canvas.Handle, Details, aRect, nil);
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawCellText(aCol, aRow: Integer; aRect: TRect;
|
|
aState: TGridDrawState; aText: String);
|
|
var
|
|
Rtxt, Rrot, R: TRect;
|
|
angle: Double;
|
|
ts: TTextStyle;
|
|
begin
|
|
ts := Canvas.TextStyle;
|
|
|
|
if Canvas.Font.Orientation = 0 then
|
|
begin
|
|
dec(ARect.Right, varCellPadding);
|
|
case Canvas.TextStyle.Alignment of
|
|
Classes.taLeftJustify: Inc(ARect.Left, varCellPadding);
|
|
Classes.taRightJustify: Dec(ARect.Right, 1);
|
|
end;
|
|
case Canvas.TextStyle.Layout of
|
|
tlTop: Inc(ARect.Top, varCellPadding);
|
|
tlBottom: Dec(ARect.Bottom, varCellPadding);
|
|
end;
|
|
end else
|
|
begin
|
|
angle := Canvas.Font.Orientation * pi / 1800;
|
|
Rtxt.TopLeft := Point(0, 0);
|
|
Rtxt.BottomRight := TPoint(Canvas.TextExtent(aText));
|
|
Rrot := RotateRect(Rtxt.Width, Rtxt.Height, angle);
|
|
R := Rrot;
|
|
case Canvas.TextStyle.Alignment of
|
|
taLeftJustify: OffsetRect(R, -Rrot.Left + varCellPadding, 0);
|
|
taCenter: OffsetRect(R, (ARect.Width - Rrot.Width) div 2 - Rrot.Left, 0);
|
|
taRightJustify: OffsetRect(R, ARect.Width - Rrot.Right - varCellPadding, 0);
|
|
end;
|
|
case Canvas.TextStyle.Layout of
|
|
tlTop: OffsetRect(R, 0, -Rrot.Top + varCellPadding);
|
|
tlCenter: OffsetRect(R, 0, (ARect.Height - Rrot.Height) div 2 - Rrot.Top);
|
|
tlBottom: OffsetRect(R, 0, ARect.Height - Rrot.Bottom - varCellPadding);
|
|
end;
|
|
OffsetRect(R, -Rrot.Left, -Rrot.Top);
|
|
OffsetRect(R, ARect.Left, ARect.Top);
|
|
ARect := R;
|
|
ts.Clipping := false;
|
|
ts.Layout := tlTop;
|
|
ts.Alignment := taLeftJustify;
|
|
end;
|
|
|
|
if ARect.Right<ARect.Left then
|
|
ARect.Right:=ARect.Left;
|
|
if ARect.Left>ARect.Right then
|
|
ARect.Left:=ARect.Right;
|
|
if ARect.Bottom<ARect.Top then
|
|
ARect.Bottom:=ARect.Top;
|
|
if ARect.Top>ARect.Bottom then
|
|
ARect.Top:=ARect.Bottom;
|
|
|
|
if (ARect.Left<>ARect.Right) and (ARect.Top<>ARect.Bottom) then
|
|
Canvas.TextRect(aRect,ARect.Left,ARect.Top, aText, ts);
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawGridCheckboxBitmaps(const aCol,aRow: Integer;
|
|
const aRect: TRect; const aState: TCheckboxState);
|
|
const
|
|
arrtb:array[TCheckboxState] of TThemedButton =
|
|
(tbCheckBoxUncheckedNormal, tbCheckBoxCheckedNormal, tbCheckBoxMixedNormal);
|
|
var
|
|
ChkBitmap: TBitmap;
|
|
XPos,YPos: Integer;
|
|
Details: TThemedElementDetails;
|
|
PaintRect: TRect;
|
|
CSize: TSize;
|
|
bmpAlign: TAlignment;
|
|
bmpLayout: TTextLayout;
|
|
ChkIL: TCustomImageList;
|
|
ChkII: TImageIndex;
|
|
ChkILRes: TScaledImageListResolution;
|
|
begin
|
|
|
|
if Columns.Enabled then
|
|
begin
|
|
bmpAlign := GetColumnAlignment(aCol, false);
|
|
bmpLayout := GetColumnLayout(aCol, false);
|
|
end else
|
|
begin
|
|
bmpAlign := taCenter;
|
|
bmpLayout := Canvas.TextStyle.Layout;
|
|
end;
|
|
|
|
Details.State := -1;
|
|
ChkIL := nil;
|
|
ChkILRes := TScaledImageListResolution.Create(nil, 0);
|
|
ChkII := -1;
|
|
ChkBitmap := nil;
|
|
|
|
GetImageForCheckBox(aCol, aRow, AState, ChkIL, ChkII, ChkBitmap);
|
|
if Assigned(ChkBitmap) then
|
|
CSize := Size(ChkBitmap.Width, ChkBitmap.Height)
|
|
else if (Assigned(ChkIL) and (ChkII>=0)) then
|
|
begin
|
|
ChkILRes := ChkIL.ResolutionForPPI[ChkIL.Width, Font.PixelsPerInch, GetCanvasScaleFactor];
|
|
CSize := ChkILRes.Size;
|
|
end else
|
|
begin
|
|
Details := ThemeServices.GetElementDetails(arrtb[AState]);
|
|
// Maybe: CSize := ThemeServices.GetDetailSizeForPPI(Details, Font.PixelsPerInch);
|
|
CSize := ThemeServices.GetDetailSize(Details);
|
|
CSize.cx := MulDiv(CSize.cx, Font.PixelsPerInch, Screen.PixelsPerInch);
|
|
CSize.cy := MulDiv(CSize.cy, Font.PixelsPerInch, Screen.PixelsPerInch);
|
|
end;
|
|
|
|
case bmpAlign of
|
|
taCenter: PaintRect.Left := (aRect.Left + aRect.Right - CSize.cx) div 2;
|
|
taLeftJustify: PaintRect.Left := ARect.Left + varCellPadding;
|
|
taRightJustify: PaintRect.Left := ARect.Right - CSize.Cx - varCellPadding - 1;
|
|
end;
|
|
|
|
case bmpLayout of
|
|
tlTop : PaintRect.Top := aRect.Top + varCellPadding;
|
|
tlCenter : PaintRect.Top := (aRect.Top + aRect.Bottom - CSize.cy) div 2;
|
|
tlBottom : PaintRect.Top := aRect.Bottom - varCellPadding - CSize.cy - 1;
|
|
end;
|
|
PaintRect := Bounds(PaintRect.Left, PaintRect.Top, CSize.cx, CSize.cy);
|
|
|
|
if Details.State>=0 then
|
|
ThemeServices.DrawElement(Canvas.Handle, Details, PaintRect, nil)
|
|
else
|
|
if Assigned(ChkBitmap) then
|
|
Canvas.StretchDraw(PaintRect, ChkBitmap)
|
|
else
|
|
if Assigned(ChkILRes.Resolution) then
|
|
ChkILRes.StretchDraw(Canvas, ChkII, PaintRect);
|
|
end;
|
|
|
|
procedure TCustomGrid.DrawButtonCell(const aCol, aRow: Integer; aRect: TRect;
|
|
const aState: TGridDrawState);
|
|
var
|
|
details: TThemedElementDetails;
|
|
begin
|
|
Dec(aRect.Right);
|
|
Dec(aRect.Bottom);
|
|
if gdPushed in aState then
|
|
Details := ThemeServices.GetElementDetails(tbPushButtonPressed)
|
|
else
|
|
if gdHot in aState then
|
|
Details := ThemeServices.GetElementDetails(tbPushButtonHot)
|
|
else
|
|
Details := ThemeServices.GetElementDetails(tbPushButtonNormal);
|
|
ThemeServices.DrawElement(Canvas.Handle, Details, aRect, nil);
|
|
end;
|
|
|
|
procedure TCustomGrid.OnTitleFontChanged(Sender: TObject);
|
|
begin
|
|
FTitleFontIsDefault := False;
|
|
if FColumns.Enabled then begin
|
|
FColumns.TitleFontChanged;
|
|
ColumnsChanged(nil);
|
|
end else
|
|
VisualChange;
|
|
end;
|
|
|
|
procedure TCustomGrid.ReadColumns(Reader: TReader);
|
|
begin
|
|
Columns.Clear;
|
|
Reader.ReadValue;
|
|
Reader.ReadCollection(Columns);
|
|
end;
|
|
|
|
procedure TCustomGrid.ReadColWidths(Reader: TReader);
|
|
var
|
|
i: integer;
|
|
begin
|
|
with Reader do begin
|
|
ReadListBegin;
|
|
for i:=0 to ColCount-1 do
|
|
ColWidths[I] := ReadInteger;
|
|
ReadListEnd;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.ReadRowHeights(Reader: TReader);
|
|
var
|
|
i: integer;
|
|
begin
|
|
with Reader do begin
|
|
ReadListBegin;
|
|
for i:=0 to RowCount-1 do
|
|
RowHeights[I] := ReadInteger;
|
|
ReadListEnd;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.WMEraseBkgnd(var message: TLMEraseBkgnd);
|
|
begin
|
|
message.Result:=1;
|
|
end;
|
|
|
|
procedure TCustomGrid.WMGetDlgCode(var Msg: TLMNoParams);
|
|
begin
|
|
Msg.Result := DLGC_WANTARROWS or DLGC_WANTCHARS;
|
|
if goTabs in Options then Msg.Result:= Msg.Result or DLGC_WANTTAB;
|
|
end;
|
|
|
|
procedure TCustomGrid.WMHScroll(var message: TLMHScroll);
|
|
var
|
|
SP: TPoint;
|
|
begin
|
|
SP := GetPxTopLeft;
|
|
|
|
case message.ScrollCode of
|
|
SB_THUMBPOSITION,
|
|
SB_THUMBTRACK: begin
|
|
if (message.ScrollCode=SB_THUMBPOSITION) or (goThumbTracking in Options) then
|
|
begin
|
|
if BiDiMode = bdRightToLeft then
|
|
TrySmoothScrollBy(FGCache.HScrollBarNetRange-message.Pos-SP.x, 0)
|
|
else
|
|
TrySmoothScrollBy(message.Pos-SP.x, 0);
|
|
end;
|
|
message.Result := 0;
|
|
end;
|
|
SB_PAGELEFT: TrySmoothScrollBy(-(ClientWidth-FGCache.FixedWidth)*RTLSign, 0);
|
|
SB_PAGERIGHT: TrySmoothScrollBy((ClientWidth-FGCache.FixedWidth)*RTLSign, 0);
|
|
SB_LINELEFT: TrySmoothScrollBy(-DefaultColWidth*RTLSign, 0);
|
|
SB_LINERIGHT: TrySmoothScrollBy(DefaultColWidth*RTLSign, 0);
|
|
end;
|
|
|
|
if EditorMode then
|
|
EditorPos;
|
|
end;
|
|
|
|
procedure TCustomGrid.WMVScroll(var message: TLMVScroll);
|
|
var
|
|
SP: TPoint;
|
|
begin
|
|
SP := GetPxTopLeft;
|
|
|
|
case message.ScrollCode of
|
|
SB_THUMBPOSITION,
|
|
SB_THUMBTRACK: begin
|
|
if (message.ScrollCode=SB_THUMBPOSITION) or (goThumbTracking in Options) then
|
|
TrySmoothScrollBy(0, message.Pos-SP.y);
|
|
message.Result := 0;
|
|
end;
|
|
SB_PAGEUP: TrySmoothScrollBy(0, -(ClientHeight-FGCache.FixedHeight));
|
|
SB_PAGEDOWN: TrySmoothScrollBy(0, ClientHeight-FGCache.FixedHeight);
|
|
SB_LINEUP: TrySmoothScrollBy(0, -DefaultRowHeight);
|
|
SB_LINEDOWN: TrySmoothScrollBy(0, DefaultRowHeight);
|
|
end;
|
|
|
|
if EditorMode then
|
|
EditorPos;
|
|
end;
|
|
|
|
procedure TCustomGrid.WMKillFocus(var message: TLMKillFocus);
|
|
begin
|
|
if csDestroying in ComponentState then
|
|
exit;
|
|
{$ifdef dbgGrid}
|
|
DbgOut('*** grid.WMKillFocus, FocusedWnd=%x WillFocus=',[Message.FocusedWnd]);
|
|
if EditorMode and (Message.FocusedWnd = FEditor.Handle) then
|
|
DebugLn('Editor')
|
|
else begin
|
|
DbgOut('ExternalWindow: ');
|
|
if GetProp(Message.FocusedWnd, 'WinControl')<>nil then
|
|
DebugLn(dbgsname(TObject(GetProp(Message.FocusedWnd, 'WinControl'))))
|
|
else
|
|
DebugLn(' Unknown Window');
|
|
end;
|
|
{$endif}
|
|
inherited WMKillFocus(Message);
|
|
InvalidateFocused;
|
|
end;
|
|
|
|
procedure TCustomGrid.WMSetFocus(var message: TLMSetFocus);
|
|
begin
|
|
{$ifdef dbgGrid}
|
|
DbgOut('*** grid.WMSetFocus, FocusedWnd=', dbgs(Message.FocusedWnd),'[',dbgs(pointer(Message.FocusedWnd)),'] ');
|
|
if EditorMode and (Message.FocusedWnd = FEditor.Handle) then
|
|
DebugLn('Editor')
|
|
else begin
|
|
if Message.FocusedWnd=Self.Handle then
|
|
DebugLn('Same Grid!')
|
|
else
|
|
DebugLn('ExternalWindow');
|
|
end;
|
|
{$endif}
|
|
inherited WMSetFocus(Message);
|
|
InvalidateFocused;
|
|
end;
|
|
|
|
procedure TCustomGrid.WMSize(var Message: TLMSize);
|
|
begin
|
|
if gfUpdatingScrollbar in FGridFlags then // ignore WMSize when updating scrollbars. issue #31715
|
|
Exit;
|
|
inherited WMSize(Message);
|
|
end;
|
|
|
|
class procedure TCustomGrid.WSRegisterClass;
|
|
begin
|
|
inherited WSRegisterClass;
|
|
RegisterCustomGrid;
|
|
end;
|
|
|
|
procedure TCustomGrid.AddSelectedRange;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
if (goRangeSelect in Options) and (FRangeSelectMode = rsmMulti) then begin
|
|
n := Length(FSelections);
|
|
SetLength(FSelections, n+1);
|
|
FSelections[n] := FRange;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.AdjustClientRect(var ARect: TRect);
|
|
begin
|
|
inherited AdjustClientRect(ARect);
|
|
include(FGridFlags, gfClientRectChange);
|
|
end;
|
|
|
|
procedure TCustomGrid.WndProc(var TheMessage: TLMessage);
|
|
begin
|
|
{$ifdef GridTraceMsg}
|
|
TransMsg('GRID: ', TheMessage);
|
|
{$endif}
|
|
case TheMessage.Msg of
|
|
LM_HSCROLL, LM_VSCROLL:
|
|
if csDesigning in ComponentState then
|
|
exit;
|
|
{$IFDEF MSWINDOWS}
|
|
// Ignore LM_SIZE while another sizing is being processed.
|
|
// Windows sends WM_SIZE when showing/hiding scrollbars.
|
|
// Scrollbars can be shown/hidden when processing DoOnChangeBounds.
|
|
LM_SIZE:
|
|
if gfUpdatingSize in FGridFlags then
|
|
exit;
|
|
{$ENDIF}
|
|
end;
|
|
inherited WndProc(TheMessage);
|
|
if not (FGridState in [gsColMoving, gsRowMoving]) then //For sure if MouseUp event is lost
|
|
FreeAndNil(FScroller);
|
|
end;
|
|
|
|
procedure TCustomGrid.CreateWnd;
|
|
begin
|
|
//DebugLn('TCustomGrid.CreateWnd ',DbgSName(Self));
|
|
inherited CreateWnd;
|
|
FVSbVisible := Ord(GetScrollbarvisible(Handle, SB_Vert));
|
|
FHSbVisible := Ord(GetScrollbarvisible(Handle, SB_Horz));
|
|
CheckPosition;
|
|
VisualChange;
|
|
end;
|
|
|
|
{ Scroll grid to the given Topleft[aCol,aRow] as needed }
|
|
procedure TCustomGrid.TryScrollTo(aCol, aRow: Integer; ClearColOff,
|
|
ClearRowOff: Boolean);
|
|
var
|
|
TryTL: TPoint;
|
|
NewCol,NewRow: Integer;
|
|
TLChange: Boolean;
|
|
begin
|
|
TryTL:=ScrollGrid(False,aCol, aRow);
|
|
TLChange := (TryTL <> FTopLeft);
|
|
if TLChange
|
|
or ((TryTL <> Point(aCol, aRow)) and (goSmoothScroll in Options))
|
|
or (ClearColOff and (FGCache.TLColOff<>0))
|
|
or (ClearRowOff and (FGCache.TLRowOff<>0)) then
|
|
begin
|
|
NewCol := TryTL.X - FTopLeft.X + Col;
|
|
NewRow := TryTL.Y - FTopLeft.Y + Row;
|
|
FTopLeft:=TryTL;
|
|
if ClearColOff then
|
|
FGCache.TLColOff := 0;
|
|
if ClearRowOff then
|
|
FGCache.TLRowOff := 0;
|
|
if (aCol>TryTL.X) and (goSmoothScroll in Options) then
|
|
FGCache.TLColOff := FGCache.MaxTLOffset.X;
|
|
if (aRow>TryTL.Y) and (goSmoothScroll in Options) then
|
|
FGCache.TLRowOff := FGCache.MaxTLOffset.Y;
|
|
{$ifdef dbgscroll}
|
|
DebugLn('TryScrollTo: TopLeft=%s NewCol=%d NewRow=%d',
|
|
[dbgs(FTopLeft), NewCol, NewRow]);
|
|
{$endif}
|
|
// To-Do: move rect with ScrollBy_WS and invalidate only new (not scrolled) rects
|
|
if TLChange then
|
|
doTopleftChange(False)
|
|
else
|
|
VisualChange;
|
|
if goScrollKeepVisible in Options then
|
|
MoveNextSelectable(False, NewCol, NewRow);
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.TrySmoothScrollBy(aColDelta, aRowDelta: Integer): Boolean;
|
|
var
|
|
OldTopLeft, OldTopLeftXY, NewTopLeftXY, OldOff: TPoint;
|
|
begin
|
|
if (aColDelta=0) and (aRowDelta=0) then
|
|
Exit(True);
|
|
|
|
OldTopLeft := FTopLeft;
|
|
OldTopLeftXY := GetPxTopLeft;
|
|
OldOff := Point(FGCache.TLColOff, FGCache.TLRowOff);
|
|
|
|
Inc(FGCache.TLColOff, aColDelta);
|
|
Inc(FGCache.TLRowOff, aRowDelta);
|
|
|
|
while (FTopLeft.x < GCache.MaxTopLeft.x) and (FGCache.TLColOff >= ColWidths[FTopLeft.x]) do
|
|
begin
|
|
Dec(FGCache.TLColOff, ColWidths[FTopLeft.x]);
|
|
Inc(FTopLeft.x);
|
|
end;
|
|
while (FTopLeft.x > FixedCols) and (FGCache.TLColOff < 0) do
|
|
begin
|
|
Dec(FTopLeft.x);
|
|
Inc(FGCache.TLColOff, ColWidths[FTopLeft.x]);
|
|
end;
|
|
|
|
while (FTopLeft.y < GCache.MaxTopLeft.y) and (FGCache.TLRowOff >= RowHeights[FTopLeft.y]) do
|
|
begin
|
|
Dec(FGCache.TLRowOff, RowHeights[FTopLeft.y]);
|
|
Inc(FTopLeft.y);
|
|
end;
|
|
while (FTopLeft.y > FixedRows) and (FGCache.TLRowOff < 0) do
|
|
begin
|
|
Dec(FTopLeft.y);
|
|
Inc(FGCache.TLRowOff, RowHeights[FTopLeft.y]);
|
|
end;
|
|
|
|
FGCache.TLColOff := Max(0, FGCache.TLColOff);
|
|
FGCache.TLRowOff := Max(0, FGCache.TLRowOff);
|
|
if FTopLeft.x=FGCache.MaxTopLeft.x then
|
|
FGCache.TLColOff := Min(FGCache.MaxTLOffset.x, FGCache.TLColOff);
|
|
if FTopLeft.y=FGCache.MaxTopLeft.y then
|
|
FGCache.TLRowOff := Min(FGCache.MaxTLOffset.y, FGCache.TLRowOff);
|
|
|
|
if not GetSmoothScroll(SB_Horz) then
|
|
FGCache.TLColOff := 0;
|
|
if not GetSmoothScroll(SB_Vert) then
|
|
FGCache.TLRowOff := 0;
|
|
|
|
if OldTopLeft <> FTopLeft then begin
|
|
TopLeftChanged;
|
|
if goScrollKeepVisible in Options then
|
|
MoveNextSelectable(False, FTopLeft.x - oldTopLeft.x + col,
|
|
FTopLeft.y - oldTopLeft.y + row);
|
|
end;
|
|
|
|
NewTopLeftXY := GetPxTopLeft;
|
|
ScrollBy((OldTopLeftXY.x-NewTopLeftXY.x)*RTLSign, OldTopLeftXY.y-NewTopLeftXY.y);
|
|
|
|
//Result is false if this function failed due to a too high/wide cell (applicable only if goSmoothScroll not used)
|
|
Result := (OldTopLeftXY <> NewTopLeftXY)
|
|
or ((NewTopLeftXY.x = 0) and (aColDelta < 0))
|
|
or ((FTopLeft.x = FGCache.MaxTopLeft.x) and (FGCache.TLColOff = FGCache.MaxTLOffset.x) and (aColDelta > 0))
|
|
or ((NewTopLeftXY.y = 0) and (aRowDelta < 0))
|
|
or ((FTopLeft.y = FGCache.MaxTopLeft.y) and (FGCache.TLRowOff = FGCache.MaxTLOffset.y) and (aRowDelta > 0));
|
|
end;
|
|
|
|
procedure TCustomGrid.SetGridLineWidth(const AValue: Integer);
|
|
begin
|
|
if FGridLineWidth = AValue then
|
|
exit;
|
|
FGridLineWidth := AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCustomGrid.UpdateCachedSizes;
|
|
var
|
|
i: Integer;
|
|
TLChanged: Boolean;
|
|
begin
|
|
if AutoFillColumns then
|
|
InternalAutoFillColumns;
|
|
|
|
// Calculate New Cached Values
|
|
FGCache.GridWidth:=0;
|
|
FGCache.FixedWidth:=0;
|
|
for i:=0 to ColCount-1 do begin
|
|
FGCache.AccumWidth[i]:=FGCache.GridWidth;
|
|
FGCache.GridWidth:=FGCache.GridWidth + GetColWidths(i);
|
|
if i<FixedCols then
|
|
FGCache.FixedWidth:=FGCache.GridWidth;
|
|
end;
|
|
|
|
FGCache.Gridheight:=0;
|
|
FGCache.FixedHeight:=0;
|
|
for i:=0 to RowCount-1 do begin
|
|
FGCache.AccumHeight[i]:=FGCache.Gridheight;
|
|
FGCache.Gridheight:=FGCache.Gridheight+GetRowHeights(i);
|
|
if i<FixedRows then
|
|
FGCache.FixedHeight:=FGCache.GridHeight;
|
|
end;
|
|
|
|
FGCache.ClientRect := ClientRect;
|
|
FGCache.ClientWidth := ClientWidth;
|
|
FGCache.ClientHeight := ClientHeight;
|
|
|
|
FGCache.ScrollWidth := FGCache.ClientWidth-FGCache.FixedWidth;
|
|
FGCache.ScrollHeight := FGCache.ClientHeight-FGCache.FixedHeight;
|
|
CalcMaxTopLeft;
|
|
|
|
TLChanged := False;
|
|
if fTopLeft.y > FGCache.MaxTopLeft.y then
|
|
begin
|
|
fTopLeft.y := FGCache.MaxTopLeft.y;
|
|
FGCache.TLRowOff := FGCache.MaxTLOffset.y;
|
|
TLChanged := True;
|
|
end else
|
|
if FTopLeft.y < FixedRows then
|
|
begin
|
|
fTopLeft.y := FixedRows;
|
|
TLChanged := True;
|
|
end;
|
|
if fTopLeft.x > FGCache.MaxTopLeft.x then
|
|
begin
|
|
fTopLeft.x := FGCache.MaxTopLeft.x;
|
|
FGCache.TLColOff := FGCache.MaxTLOffset.x;
|
|
TLChanged := True;
|
|
end else
|
|
if FTopLeft.x < FixedCols then
|
|
begin
|
|
fTopLeft.x := FixedCols;
|
|
TLChanged := True;
|
|
end;
|
|
if TopRow=FGCache.MaxTopLeft.y then
|
|
FGCache.TLRowOff := Min(FGCache.TLRowOff, FGCache.MaxTLOffset.y)
|
|
else
|
|
FGCache.TLRowOff := Min(FGCache.TLRowOff, RowHeights[TopRow]);
|
|
if LeftCol=FGCache.MaxTopLeft.x then
|
|
FGCache.TLColOff := Min(FGCache.TLColOff, FGCache.MaxTLOffset.x)
|
|
else
|
|
FGCache.TLColOff := Min(FGCache.TLColOff, ColWidths[LeftCol]);
|
|
if TLChanged then
|
|
TopLeftChanged;
|
|
|
|
{$ifdef dbgVisualChange}
|
|
DebugLn('TCustomGrid.updateCachedSizes: ');
|
|
with FGCache do
|
|
DebugLn(' GWidth=%d GHeight=%d FWidth=%d FHeight=%d CWidth=%d CHeight=%d MTL.X=%d MTL.Y=%d',
|
|
[GridWidth,GridHeight,FixedWidth,FixedHeight,ClientWidth,ClientHeight,
|
|
MaxTopLeft.X, MaxTopLeft.Y]);
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.GetSBVisibility(out HsbVisible,VsbVisible:boolean);
|
|
var
|
|
autoVert,autoHorz: boolean;
|
|
ClientW,ClientH,ExtraW,ExtraH: Integer;
|
|
BarW,BarH: Integer;
|
|
begin
|
|
AutoVert := ScrollBarAutomatic(ssVertical);
|
|
AutoHorz := ScrollBarAutomatic(ssHorizontal);
|
|
|
|
// get client bounds free of bars
|
|
ClientW := ClientWidth;
|
|
ClientH := ClientHeight;
|
|
BarW := GetSystemMetrics(SM_CXVSCROLL) +
|
|
GetSystemMetrics(SM_SWSCROLLBARSPACING);
|
|
if ScrollBarIsVisible(SB_VERT) then
|
|
ClientW := ClientW + BarW;
|
|
BarH := GetSystemMetrics(SM_CYHSCROLL) +
|
|
GetSystemMetrics(SM_SWSCROLLBARSPACING);
|
|
if ScrollBarIsVisible(SB_HORZ) then
|
|
ClientH := ClientH + BarH;
|
|
ExtraW := 0;
|
|
if goScrollToLastCol in FOptions2 then
|
|
begin
|
|
Inc(ExtraW, ClientWidth - FGCache.FixedWidth);
|
|
if ColCount>FixedCols then
|
|
Dec(ExtraW, ColWidths[ColCount-1]);
|
|
end;
|
|
ExtraH := 0;
|
|
if goScrollToLastRow in FOptions2 then
|
|
begin
|
|
Inc(ExtraH, ClientHeight - FGCache.FixedHeight);
|
|
if RowCount>FixedRows then
|
|
Dec(ExtraH, RowHeights[RowCount-1]);
|
|
end;
|
|
|
|
// first find out if scrollbars need to be visible by
|
|
// comparing against client bounds free of bars
|
|
HsbVisible := (FScrollBars in [ssHorizontal, ssBoth]) or
|
|
(AutoHorz and (FGCache.GridWidth+ExtraW>ClientW));
|
|
|
|
VsbVisible := (FScrollBars in [ssVertical, ssBoth]) or
|
|
(AutoVert and (FGCache.GridHeight+ExtraH>ClientH));
|
|
|
|
// then for automatic scrollbars check if grid bounds are
|
|
// in some part of area occupied by scrollbars
|
|
if ExtraW>0 then
|
|
Dec(ExtraW, BarW);
|
|
if not HsbVisible and AutoHorz and VsbVisible then
|
|
HsbVisible := FGCache.GridWidth+ExtraW > (ClientW-BarW);
|
|
|
|
if ExtraH>0 then
|
|
Dec(ExtraH, BarH);
|
|
if not VsbVisible and AutoVert and HsbVisible then
|
|
VsbVisible := FGCache.GridHeight+ExtraH > (ClientH-BarH);
|
|
|
|
if AutoHorz then
|
|
HsbVisible := HsbVisible and not AutoFillColumns;
|
|
|
|
// update new cached client values according to visibility
|
|
// of scrollbars
|
|
if HsbVisible then
|
|
FGCache.ClientHeight := ClientH - BarH;
|
|
if VsbVisible then
|
|
FGCache.ClientWidth := ClientW - BarW;
|
|
|
|
{$ifdef dbgscroll}
|
|
DebugLn('TCustomGrid.GetSBVisibility:');
|
|
DebugLn([' Horz=',HsbVisible,' GW=',FGCache.GridWidth,
|
|
' CW=',ClientWidth,' CCW=',FGCache.ClientWidth,' BarW=',BarW]);
|
|
DebugLn([' Vert=',VsbVisible,' GH=',FGCache.GridHeight,
|
|
' CH=',ClientHeight,' CCH=',FGCache.ClientHeight,' BarH=',BarH]);
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.GetSBRanges(const HsbVisible, VsbVisible: boolean; out
|
|
HsbRange, VsbRange, HsbPage, VsbPage, HsbPos, VsbPos: Integer);
|
|
begin
|
|
HsbRange := 0;
|
|
HsbPos := 0;
|
|
if HsbVisible then
|
|
begin
|
|
if not GetSmoothScroll(SB_Horz) then
|
|
begin
|
|
if IsColumnIndexValid(FGCache.MaxTopLeft.x) then
|
|
HsbRange := FGCache.AccumWidth[FGCache.MaxTopLeft.x]+ClientWidth-FGCache.FixedWidth
|
|
end else
|
|
begin
|
|
HsbRange:=GridWidth - GetBorderWidth;
|
|
if goScrollToLastCol in FOptions2 then
|
|
begin
|
|
Inc(HsbRange, ClientWidth - FGCache.FixedWidth);
|
|
if ColCount>FixedCols then
|
|
Dec(HsbRange, ColWidths[ColCount-1]);
|
|
end;
|
|
end;
|
|
if IsColumnIndexValid(FTopLeft.x) then
|
|
HsbPos := FGCache.AccumWidth[FTopLeft.x]+FGCache.TLColOff-FGCache.FixedWidth;
|
|
end;
|
|
|
|
VsbRange := 0;
|
|
VsbPos := 0;
|
|
if VsbVisible then
|
|
begin
|
|
if not GetSmoothScroll(SB_Vert) then
|
|
begin
|
|
if IsRowIndexValid(FGCache.MaxTopLeft.y) then
|
|
VsbRange := FGCache.AccumHeight[FGCache.MaxTopLeft.y]+ClientHeight-FGCache.FixedHeight
|
|
end else
|
|
begin
|
|
VSbRange:= GridHeight - GetBorderWidth;
|
|
if goScrollToLastRow in FOptions2 then
|
|
begin
|
|
Inc(VsbRange, ClientHeight - FGCache.FixedHeight);
|
|
if RowCount>FixedRows then
|
|
Dec(VsbRange, RowHeights[RowCount-1]);
|
|
end;
|
|
end;
|
|
if IsRowIndexValid(FTopLeft.y) then
|
|
VsbPos := FGCache.AccumHeight[FTopLeft.y]+FGCache.TLRowOff-FGCache.FixedHeight;
|
|
end;
|
|
|
|
HsbPage := ClientWidth;
|
|
VSbPage := ClientHeight;
|
|
|
|
FGCache.HScrollBarNetRange := HsbRange-HsbPage;
|
|
|
|
{$ifdef dbgscroll}
|
|
DebugLn('GetSBRanges: HRange=%d HPage=%d HPos=%d VRange=%d VPage=%d VPos=%d',
|
|
[HSbRange,HsbPage,HsbPos, VsbRange, VsbPage, VsbPos]);
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.GetSelectedState(AState: TGridDrawState; out
|
|
IsSelected: boolean);
|
|
begin
|
|
IsSelected := (gdSelected in aState);
|
|
if IsSelected and (gdFocused in aState) then
|
|
IsSelected := (goDrawFocusSelected in Options) or
|
|
((goRowSelect in Options) and not (goRelaxedRowSelect in Options));
|
|
end;
|
|
|
|
procedure TCustomGrid.UpdateSBVisibility;
|
|
var
|
|
HSbVisible, VSbVisible: boolean;
|
|
begin
|
|
GetSBVisibility(HSbVisible, VSbVisible);
|
|
ScrollBarShow(SB_VERT, VSbVisible);
|
|
ScrollBarShow(SB_HORZ, HSbVisible);
|
|
end;
|
|
|
|
procedure TCustomGrid.UpdateSizes;
|
|
begin
|
|
if (FUpdateCount<>0) then
|
|
exit;
|
|
|
|
Include(FGridFlags, gfVisualChange);
|
|
|
|
UpdateCachedSizes;
|
|
CacheVisibleGrid;
|
|
CalcScrollbarsRange;
|
|
end;
|
|
|
|
procedure TCustomGrid.UpdateSelectionRange;
|
|
begin
|
|
if goRowSelect in Options then begin
|
|
FRange:=Rect(FFixedCols, FRow, ColCount-1, FRow);
|
|
end
|
|
else
|
|
FRange:=Rect(FCol,FRow,FCol,FRow);
|
|
end;
|
|
|
|
procedure TCustomGrid.WriteColumns(Writer: TWriter);
|
|
begin
|
|
if Columns.IsDefault then
|
|
Writer.WriteCollection(nil)
|
|
else
|
|
Writer.WriteCollection(Columns);
|
|
end;
|
|
|
|
procedure TCustomGrid.WriteColWidths(Writer: TWriter);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
with writer do begin
|
|
WriteListBegin;
|
|
for i:=0 to ColCount-1 do
|
|
WriteInteger(ColWidths[i]);
|
|
WriteListEnd;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.WriteRowHeights(Writer: TWriter);
|
|
var
|
|
i: integer;
|
|
begin
|
|
with writer do begin
|
|
WriteListBegin;
|
|
for i:=0 to RowCount-1 do
|
|
WriteInteger(RowHeights[i]);
|
|
WriteListEnd;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.CheckFixedCount(aCol,aRow,aFCol,aFRow: Integer);
|
|
begin
|
|
if AFRow<0 then
|
|
raise EGridException.Create('FixedRows<0');
|
|
if AFCol<0 then
|
|
raise EGridException.Create('FixedCols<0');
|
|
|
|
if csLoading in ComponentState then
|
|
exit;
|
|
|
|
if (aCol=0)and(aFCol=0) then // fixed grid
|
|
else if (aFCol>ACol) then
|
|
raise EGridException.Create(rsFixedColsTooBig);
|
|
|
|
if (aRow=0)and(aFRow=0) then // fixed grid
|
|
else if (aFRow>ARow) then
|
|
raise EGridException.Create(rsFixedRowsTooBig);
|
|
end;
|
|
|
|
procedure TCustomGrid.CheckCount(aNewColCount, aNewRowCount: Integer; FixEditor: boolean=true);
|
|
var
|
|
NewCol,NewRow: Integer;
|
|
begin
|
|
if HandleAllocated then begin
|
|
if Col >= aNewColCount then NewCol := aNewColCount-1
|
|
else NewCol := Col;
|
|
if Row >= aNewRowCount then NewRow := aNewRowCount-1
|
|
else NewRow := Row;
|
|
if (NewCol>=0) and (NewRow>=0) and ((NewCol <> Col) or (NewRow <> Row)) then
|
|
begin
|
|
CheckTopleft(NewCol, NewRow , NewCol<>Col, NewRow<>Row);
|
|
if FixEditor and (aNewColCount<>FFixedCols) and (aNewRowCount<>FFixedRows) then
|
|
MoveNextSelectable(false, NewCol, NewRow);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.CheckIndex(IsColumn: Boolean; Index: Integer);
|
|
begin
|
|
if (IsColumn and not IsColumnIndexValid(Index)) or
|
|
(not IsColumn and not IsRowIndexValid(Index)) then
|
|
raise EGridException.Create(rsGridIndexOutOfRange);
|
|
end;
|
|
|
|
function TCustomGrid.CheckTopLeft(aCol,aRow: Integer; CheckCols, CheckRows: boolean): boolean;
|
|
var
|
|
OldTopLeft: TPoint;
|
|
W: Integer;
|
|
begin
|
|
OldTopLeft := FTopLeft;
|
|
Result := False;
|
|
|
|
if CheckCols and (FTopleft.X > FixedCols) then begin
|
|
W := FGCache.ScrollWidth-ColWidths[aCol]-FGCache.AccumWidth[aCol];
|
|
while (FTopleft.x > FixedCols)
|
|
and (W+FGCache.AccumWidth[FTopleft.x] >= ColWidths[FTopleft.x-1]) do
|
|
Dec(FTopleft.x);
|
|
end;
|
|
|
|
if CheckRows and (FTopleft.Y > FixedRows) then begin
|
|
W := FGCache.ScrollHeight-RowHeights[aRow]-FGCache.AccumHeight[aRow];
|
|
while (FTopleft.y > FixedRows)
|
|
and (W+FGCache.AccumHeight[FTopleft.y] >= RowHeights[FTopleft.y-1]) do
|
|
Dec(FTopleft.y);
|
|
//DebugLn('TCustomGrid.CheckTopLeft A ',DbgSName(Self),' FTopLeft=',dbgs(FTopLeft));
|
|
end;
|
|
|
|
Result := (OldTopLeft <> FTopLeft);
|
|
if Result then
|
|
doTopleftChange(False)
|
|
end;
|
|
|
|
function TCustomGrid.GetQuickColRow: TPoint;
|
|
begin
|
|
result.x := Col;
|
|
result.y := Row;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetQuickColRow(AValue: TPoint);
|
|
begin
|
|
if (AValue.x=FCol) and (AValue.y=FRow) then Exit;
|
|
if not AllowOutboundEvents then
|
|
CheckLimitsWithError(AValue.x, AValue.y);
|
|
SetColRow(aValue.x, aValue.y, true);
|
|
end;
|
|
|
|
procedure TCustomGrid.doPushCell;
|
|
begin
|
|
with FGCache do
|
|
begin
|
|
PushedCell := ClickCell;
|
|
ClickCellPushed:=True;
|
|
InvalidateCell(PushedCell.x, PushedCell.y);
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.IsCellButtonColumn(ACell: TPoint): boolean;
|
|
var
|
|
Column: TGridColumn;
|
|
begin
|
|
Column := ColumnFromGridColumn(ACell.X);
|
|
result := (Column<>nil) and (Column.ButtonStyle=cbsButtonColumn) and
|
|
(ACell.y>=FixedRows);
|
|
end;
|
|
|
|
function TCustomGrid.GetIsCellTitle(aCol, aRow: Integer): boolean;
|
|
begin
|
|
result := (FixedRows>0) and (aRow=0) {and Columns.Enabled} and (aCol>=FirstGridColumn);
|
|
// Columns.Enabled removed in order to allow sort arrows also without columns
|
|
end;
|
|
|
|
function TCustomGrid.GetIsCellSelected(aCol, aRow: Integer): boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:= (FRange.Left<=aCol) and
|
|
(aCol<=FRange.Right) and
|
|
(FRange.Top<=aRow) and
|
|
(aRow<=FRange.Bottom);
|
|
|
|
if not Result and (goRangeSelect in FOptions) and (RangeSelectMode = rsmMulti)
|
|
then
|
|
for i:=0 to High(FSelections) do
|
|
if (FSelections[i].Left <= aCol) and
|
|
(ACol <= FSelections[i].Right) and
|
|
(FSelections[i].Top <= ARow) and
|
|
(ARow <= FSelections[i].Bottom)
|
|
then begin
|
|
Result := true;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.IsEmptyRow(ARow: Integer): Boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := False;
|
|
for i:=FixedCols to ColCount-1 do
|
|
if GetCells(i, ARow)<>'' then begin
|
|
Exit;
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
function TCustomGrid.GetDefColWidth: Integer;
|
|
begin
|
|
if FDefColWidth<0 then
|
|
begin
|
|
if FRealizedDefColWidth <= 0 then
|
|
FRealizedDefColWidth := Scale96ToFont(DEFCOLWIDTH);
|
|
Result := FRealizedDefColWidth;
|
|
end else
|
|
Result := FDefColWidth;
|
|
end;
|
|
|
|
function TCustomGrid.GetDefRowHeight: Integer;
|
|
begin
|
|
if FDefRowHeight<0 then
|
|
begin
|
|
if FRealizedDefRowHeight <= 0 then
|
|
FRealizedDefRowHeight := GetDefaultRowHeight;
|
|
Result := FRealizedDefRowHeight;
|
|
end else
|
|
Result := FDefRowHeight;
|
|
end;
|
|
|
|
function TCustomGrid.GetSelectedColumn: TGridColumn;
|
|
begin
|
|
Result := ColumnFromGridColumn(Col);
|
|
end;
|
|
|
|
function TCustomGrid.IsAltColorStored: boolean;
|
|
begin
|
|
result := FAlternateColor <> Color;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetAlternateColor(const AValue: TColor);
|
|
begin
|
|
if FAlternateColor=AValue then exit;
|
|
FAlternateColor:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
function TCustomGrid.GetEditorBorderStyle: TBorderStyle;
|
|
begin
|
|
result := bsSingle;
|
|
if (FEditor = FStringEditor) or (FEditor = FButtonStringEditor) then
|
|
Result := FStringEditor.BorderStyle
|
|
else if FEditor = FPickListEditor then
|
|
Result := FPickListEditor.BorderStyle;
|
|
end;
|
|
|
|
function TCustomGrid.GetBorderWidth: Integer;
|
|
begin
|
|
if InternalNeedBorder then
|
|
Result := 1
|
|
else
|
|
Result := 0
|
|
end;
|
|
|
|
procedure TCustomGrid.GetTitleImageInfo(aColumnIndex: Integer; out
|
|
ImgIndex: Integer; out ImgLayout: TButtonLayout);
|
|
var
|
|
c: TGridColumn;
|
|
ResName: string;
|
|
begin
|
|
c := ColumnFromGridColumn(AColumnIndex);
|
|
if (c <> nil) and (FTitleImageList <> nil) and InRange(c.Title.FImageIndex, 0, FTitleImageList.Count - 1) then
|
|
begin
|
|
ImgIndex := c.Title.FImageIndex;
|
|
ImgLayout := c.Title.ImageLayout;
|
|
end else
|
|
begin
|
|
ImgIndex := -1;
|
|
ImgLayout := blGlyphRight;
|
|
end;
|
|
if IsRightToLeft then begin
|
|
if ImgLayout = blGlyphRight then
|
|
ImgLayout := blGlyphLeft
|
|
else if ImgLayout = blGlyphLeft then
|
|
ImgLayout := blGlyphRight;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.GetSortTitleImageInfo(aColumnIndex: Integer; out
|
|
ImgList: TCustomImageList; out ImgIndex, ImgListWidth: Integer; out
|
|
NativeSortGlyphs: Boolean);
|
|
var
|
|
ResName: string;
|
|
begin
|
|
NativeSortGlyphs := False;
|
|
ImgIndex := -1;
|
|
ImgList := nil;
|
|
ImgListWidth := 0;
|
|
|
|
if aColumnIndex<>FSortColumn then
|
|
Exit;
|
|
|
|
if (FTitleImageList<>nil) and (FSortOrder=soAscending) and (FAscImgInd>=0) then
|
|
begin
|
|
ImgList := FTitleImageList;
|
|
ImgListWidth := FTitleImageListWidth;
|
|
ImgIndex := FAscImgInd;
|
|
end else
|
|
if (FTitleImageList<>nil) and (FSortOrder=soDescending) and (FDescImgInd>=0) then
|
|
begin
|
|
ImgList := FTitleImageList;
|
|
ImgListWidth := FTitleImageListWidth;
|
|
ImgIndex := FDescImgInd;
|
|
end else
|
|
begin
|
|
if FSortLCLImages=nil then
|
|
begin
|
|
FSortLCLImages := TLCLGlyphs.Create(Self);
|
|
FSortLCLImages.Width := 8;
|
|
FSortLCLImages.Height := 8;
|
|
FSortLCLImages.RegisterResolutions([8, 12, 16]);
|
|
FSortLCLImages.SetWidth100Suffix(16);
|
|
end;
|
|
ImgList := FSortLCLImages;
|
|
case FSortOrder of
|
|
soAscending: ResName := 'sortasc';
|
|
soDescending: ResName := 'sortdesc';
|
|
end;
|
|
ImgIndex := FSortLCLImages.GetImageIndex(ResName);
|
|
NativeSortGlyphs := FTitleStyle = tsNative;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.GetImageForCheckBox(const aCol, aRow: Integer;
|
|
CheckBoxView: TCheckBoxState; var ImageList: TCustomImageList;
|
|
var ImageIndex: TImageIndex; var Bitmap: TBitmap);
|
|
begin
|
|
if Assigned(OnUserCheckboxBitmap) then
|
|
OnUserCheckboxBitmap(Self, aCol, aRow, CheckBoxView, Bitmap);
|
|
if (Bitmap = nil) and Assigned(OnUserCheckBoxImage) then
|
|
OnUserCheckboxImage(Self, aCol, aRow, CheckBoxView, ImageList, ImageIndex);
|
|
end;
|
|
|
|
procedure TCustomGrid.AdjustInnerCellRect(var ARect: TRect);
|
|
begin
|
|
if (GridLineWidth>0) then begin
|
|
if goHorzLine in Options then Dec(ARect.Bottom);
|
|
if goVertLine in Options then Dec(ARect.Right);
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.GetColumns: TGridColumns;
|
|
begin
|
|
result := FColumns;
|
|
end;
|
|
|
|
function TCustomGrid.CreateColumns: TGridColumns;
|
|
begin
|
|
result := TGridColumns.Create(Self, TGridColumn);
|
|
end;
|
|
|
|
procedure TCustomGrid.CheckNewCachedSizes(var AGCache:TGridDataCache);
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TCustomGrid.SetAutoFillColumns(const AValue: boolean);
|
|
begin
|
|
FAutoFillColumns := AValue;
|
|
if FAutoFillColumns then begin
|
|
VisualChange;
|
|
if FTopleft.x<>FixedCols then begin
|
|
FTopLeft.x := FixedCols;
|
|
TopLeftChanged;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetBorderColor(const AValue: TColor);
|
|
begin
|
|
if FBorderColor=AValue then exit;
|
|
FBorderColor:=AValue;
|
|
if BorderStyle<>bsNone then
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetColumnClickSorts(const AValue: boolean);
|
|
begin
|
|
if FColumnClickSorts=AValue then exit;
|
|
FColumnClickSorts:=AValue;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetColumns(const AValue: TGridColumns);
|
|
begin
|
|
FColumns.Assign(Avalue);
|
|
end;
|
|
|
|
procedure TCustomGrid.SetEditorOptions(const AValue: Integer);
|
|
begin
|
|
if FEditorOptions<>AValue then begin
|
|
if FEditor=nil then exit;
|
|
FEditorOptions:=AValue;
|
|
|
|
if FEditorOptions and EO_HOOKKEYDOWN = EO_HOOKKEYDOWN then begin
|
|
FEditor.OnKeyDown:=@EditorKeyDown;
|
|
end;
|
|
if FEditorOptions and EO_HOOKKEYPRESS = EO_HOOKKEYPRESS then begin
|
|
FEditor.OnKeyPress := @EditorKeyPress;
|
|
FEditor.OnUTF8KeyPress := @EditorUTF8KeyPress;
|
|
end;
|
|
if FEditorOptions and EO_HOOKKEYUP = EO_HOOKKEYUP then begin
|
|
FEditor.OnKeyUp := @EditorKeyUp;
|
|
end;
|
|
|
|
{$IfDef DbgGrid}
|
|
DBGOut('EditorOptions ',FEditor.Name,' ');
|
|
if FEditorOptions and EO_AUTOSIZE = EO_AUTOSIZE then DBGOut('EO_AUTOSIZE ');
|
|
if FEditorOptions and EO_HOOKKEYDOWN = EO_HOOKKEYDOWN then DBGOut('EO_HOOKKEYDOWN ');
|
|
if FEditorOptions and EO_HOOKKEYPRESS = EO_HOOKKEYPRESS then DBGOut('EO_HOOKKEYPRESS ');
|
|
if FEditorOptions and EO_HOOKKEYUP = EO_HOOKKEYUP then DBGOut('EO_HOOKKEYUP ');
|
|
if FEditorOptions and EO_SELECTALL= EO_SELECTALL then DBGOut('EO_SELECTALL ');
|
|
DebugLn;
|
|
{$Endif}
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetEditorBorderStyle(const AValue: TBorderStyle);
|
|
begin
|
|
// supposedly instances cannot access protected properties
|
|
// of parent classes, so why the next works?
|
|
{
|
|
if FEditor.BorderStyle <> AValue then begin
|
|
FEditor.BorderStyle := AValue;
|
|
if EditorMode then
|
|
EditorPos;
|
|
end;
|
|
}
|
|
if FStringEditor.BorderStyle<>AValue then begin
|
|
FStringEditor.BorderStyle := AValue;
|
|
if (FEditor = FStringEditor) and EditorMode then
|
|
EditorPos;
|
|
end;
|
|
if FPicklistEditor.BorderStyle<>AValue then begin
|
|
FPicklistEditor.BorderStyle := AValue;
|
|
if (FEditor = FPicklistEditor) and EditorMode then
|
|
EditorPos;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetAltColorStartNormal(const AValue: boolean);
|
|
begin
|
|
if FAltColorStartNormal=AValue then exit;
|
|
FAltColorStartNormal:=AValue;
|
|
if IsAltColorStored then
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetFlat(const AValue: Boolean);
|
|
begin
|
|
if FFlat=AValue then exit;
|
|
FFlat:=AValue;
|
|
if FGridBorderStyle=bsSingle then
|
|
UpdateBorderStyle
|
|
else
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetFocusRectVisible(const AValue: Boolean);
|
|
begin
|
|
if FFocusRectVisible<>AValue then begin
|
|
FFocusRectVisible := AValue;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetTitleFont(const AValue: TFont);
|
|
begin
|
|
FTitleFont.Assign(AValue);
|
|
VisualChange;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetTitleImageList(const AValue: TImageList);
|
|
begin
|
|
if FTitleImageList = AValue then exit;
|
|
FTitleImageList := AValue;
|
|
VisualChange;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetTitleImageListWidth(
|
|
const aTitleImageListWidth: Integer);
|
|
begin
|
|
if FTitleImageListWidth = aTitleImageListWidth then Exit;
|
|
FTitleImageListWidth := aTitleImageListWidth;
|
|
VisualChange;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetTitleStyle(const AValue: TTitleStyle);
|
|
begin
|
|
if FTitleStyle=AValue then exit;
|
|
FTitleStyle:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetUseXorFeatures(const AValue: boolean);
|
|
begin
|
|
if FUseXORFeatures=AValue then exit;
|
|
FUseXORFeatures:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetBorderStyle(NewStyle: TBorderStyle);
|
|
begin
|
|
if FGridBorderStyle<>NewStyle then begin
|
|
FGridBorderStyle := NewStyle;
|
|
UpdateBorderStyle;
|
|
end;
|
|
end;
|
|
|
|
{ Save to the cache the current visible grid (excluding fixed cells) }
|
|
procedure TCustomGrid.CacheVisibleGrid;
|
|
var
|
|
CellR: TRect;
|
|
begin
|
|
with FGCache do begin
|
|
VisibleGrid:=GetVisibleGrid;
|
|
with VisibleGrid do begin
|
|
ValidRows := (left>=0) and (Right>=Left) and (ColCount>0) and (RowCount>0);
|
|
ValidCols := (top>=0) and (bottom>=Top) and (ColCount>0) and (RowCount>0);
|
|
ValidGrid := ValidRows and ValidCols;
|
|
end;
|
|
FullVisibleGrid := VisibleGrid;
|
|
if ValidGrid then begin
|
|
if GetSmoothScroll(SB_Horz) and (TLColOff>0) then
|
|
FullVisibleGrid.Left := Min(FullVisibleGrid.Left+1, FullVisibleGrid.Right);
|
|
if GetSmoothScroll(SB_Vert) and (TLRowOff>0) then
|
|
FullVisibleGrid.Top := Min(FullVisibleGrid.Top+1, FullVisibleGrid.Bottom);
|
|
|
|
CellR := CellRect(FullVisibleGrid.Right, FullVisibleGrid.Bottom);
|
|
if CellR.Right>(ClientWidth+GetBorderWidth) then
|
|
FullVisibleGrid.Right := Max(FullVisibleGrid.Right-1, FullVisibleGrid.Left);
|
|
if CellR.Bottom>(ClientHeight+GetBorderWidth) then
|
|
FullVisibleGrid.Bottom := Max(FullVisibleGrid.Bottom-1, FullVisibleGrid.Top);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.CancelSelection;
|
|
begin
|
|
if (FRange.Bottom-FRange.Top>0) or
|
|
((FRange.Right-FRange.Left>0) and not (goRowSelect in Options)) then begin
|
|
InvalidateRange(FRange);
|
|
if goRowSelect in Options then
|
|
FRange:=Rect(FFixedCols, FRow, ColCount-1, FRow)
|
|
else
|
|
FRange:=Rect(FCol,FRow,FCol,FRow);
|
|
end;
|
|
SelectActive := False;
|
|
end;
|
|
|
|
function TCustomGrid.GetSelectedRange(AIndex: Integer): TGridRect;
|
|
begin
|
|
if AIndex >= Length(FSelections) then
|
|
Result := FRange
|
|
else
|
|
Result := FSelections[AIndex];
|
|
end;
|
|
|
|
function TCustomGrid.GetSelectedRangeCount: Integer;
|
|
begin
|
|
Result := Length(FSelections) + 1;
|
|
// add 1 because the current selection (FRange) is not stored in the array
|
|
end;
|
|
|
|
function TCustomGrid.GetSelection: TGridRect;
|
|
begin
|
|
Result:=FRange;
|
|
end;
|
|
|
|
function TCustomGrid.GetSpecialCursor(ACursorState: TGridCursorState): TCursor;
|
|
begin
|
|
Result := FSpecialCursors[ACursorState];
|
|
end;
|
|
|
|
function TCustomGrid.GetSmoothScroll(Which: Integer): Boolean;
|
|
begin
|
|
Result := goSmoothScroll in Options;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetColRowDragIndicatorColor(const AValue: TColor);
|
|
begin
|
|
if FColRowDragIndicatorColor = AValue then exit;
|
|
FColRowDragIndicatorColor := AValue;
|
|
if FGridState = gsColMoving then
|
|
DrawColRowMoving;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetDefaultDrawing(const AValue: Boolean);
|
|
begin
|
|
if FDefaultDrawing=AValue then exit;
|
|
FDefaultDrawing:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetFocusColor(const AValue: TColor);
|
|
begin
|
|
if FFocusColor=AValue then exit;
|
|
FFocusColor:=AValue;
|
|
InvalidateCell(FCol,FRow);
|
|
end;
|
|
|
|
procedure TCustomGrid.SetGridLineStyle(const AValue: TPenStyle);
|
|
begin
|
|
if FGridLineStyle=AValue then exit;
|
|
FGridLineStyle:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetSelectActive(const AValue: Boolean);
|
|
begin
|
|
if FSelectActive=AValue then exit;
|
|
FSelectActive:=AValue and
|
|
(not EditingAllowed(FCol) or (ExtendedSelect and not EditorAlwaysShown));
|
|
if FSelectActive then FPivot:=Point(FCol,FRow);
|
|
end;
|
|
|
|
procedure TCustomGrid.SetSelection(const AValue: TGridRect);
|
|
begin
|
|
if goRangeSelect in Options then
|
|
begin
|
|
if (AValue.Left<0)and(AValue.Top<0)and(AValue.Right<0)and(AValue.Bottom<0) then
|
|
CancelSelection
|
|
else begin
|
|
fRange:=NormalizarRect(aValue);
|
|
if fRange.Right>=ColCount then fRange.Right:=ColCount-1;
|
|
if fRange.Bottom>=RowCount then fRange.Bottom:=RowCount-1;
|
|
if fRange.Left<FixedCols then fRange.Left := FixedCols;
|
|
if fRange.Top<FixedRows then fRange.Top := FixedRows;
|
|
if goSelectionActive in Options then begin
|
|
FPivot := FRange.TopLeft;
|
|
FSelectActive := True;
|
|
MoveExtend(false, FRange.Right, FRange.Bottom, True);
|
|
end;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetSpecialCursor(ACursorState: TGridCursorState;
|
|
const AValue: TCursor);
|
|
begin
|
|
if AValue = GetSpecialCursor(ACursorState) then
|
|
exit;
|
|
FSpecialCursors[ACursorState] := AValue;
|
|
if FCursorState <> gcsDefault then
|
|
ChangeCursor(AValue, false);
|
|
end;
|
|
|
|
function TCustomGrid.doColSizing(X, Y: Integer): Boolean;
|
|
var
|
|
Offset: Integer;
|
|
|
|
procedure FindPrevColumn;
|
|
begin
|
|
Dec(FSizing.Index);
|
|
while (FSizing.Index>FixedCols) and (ColWidths[FSizing.Index]=0) do
|
|
Dec(FSizing.Index);
|
|
end;
|
|
|
|
begin
|
|
Result:=False;
|
|
|
|
with FSizing do
|
|
if gsColSizing = fGridState then begin
|
|
|
|
if not (gfSizingStarted in FGridFlags) then
|
|
if not StartColSizing(X,Y) then
|
|
exit;
|
|
Include(FGridFlags, gfSizingStarted);
|
|
|
|
if FUseXORFeatures then begin
|
|
|
|
if UseRightToLeftAlignment then begin
|
|
if (OffEnd - x) <=0 then
|
|
x:= OffEnd;
|
|
end
|
|
else
|
|
if (X-OffIni)<=0 then
|
|
X := OffIni;
|
|
|
|
if X<>PrevOffset then begin
|
|
if PrevLine then
|
|
DrawXorVertLine(PrevOffset);
|
|
DrawXorVertLine(X);
|
|
PrevLine:=True;
|
|
PrevOffset:=X;
|
|
end;
|
|
|
|
end else begin
|
|
if UseRightToLeftAlignment then
|
|
ResizeColumn(Index, OffEnd - X + DeltaOff)
|
|
else
|
|
ResizeColumn(Index, X - OffIni + DeltaOff);
|
|
end;
|
|
HeaderSizing(true, Index, X - OffIni + DeltaOff);
|
|
exit(true);
|
|
end else
|
|
if (fGridState=gsNormal) and
|
|
((Y<FGCache.FixedHeight) or (FExtendedColSizing and (Y<FGCache.MaxClientXY.Y))) and
|
|
((goFixedColSizing in Options) or ((ColCount>FixedCols) and (FlipX(X)>FGCache.FixedWidth)))
|
|
then begin
|
|
|
|
// find closest cell and cell boundaries
|
|
if (FlipX(X)>FGCache.GridWidth-1) then
|
|
Index := ColCount-1
|
|
else
|
|
OffsetToColRow(True, True, X, Index, Offset);
|
|
ColRowToOffset(True, true, Index, OffIni, OffEnd);
|
|
|
|
if OffEnd>FGCache.ClientWidth then
|
|
Offset := FGCache.ClientWidth
|
|
else if (OffEnd-X)<(X-OffIni) then begin
|
|
Offset := OffEnd;
|
|
if UseRightToLeftAlignment then
|
|
FindPrevColumn;
|
|
end else begin
|
|
Offset := OffIni;
|
|
if not UseRightToLeftAlignment then
|
|
FindPrevColumn;
|
|
end;
|
|
|
|
// check if cursor is near boundary and it's a valid column
|
|
if (Abs(Offset-x)<=varColRowBorderTolerance) then begin
|
|
if goFixedColSizing in Options then
|
|
Offset := 0
|
|
else
|
|
Offset := FFixedCols;
|
|
if Index>=Offset then begin
|
|
// start resizing
|
|
if FCursorState<>gcsColWidthChanging then begin
|
|
PrevLine := false;
|
|
PrevOffset := -1;
|
|
ChangeCursor(ColSizingCursor);
|
|
FCursorState := gcsColWidthChanging;
|
|
end;
|
|
exit(true);
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
if (FCursorState=gcsColWidthChanging) then
|
|
RestoreCursor;
|
|
end;
|
|
|
|
function TCustomGrid.doRowSizing(X, Y: Integer): Boolean;
|
|
var
|
|
Offset: Integer;
|
|
begin
|
|
Result:=False;
|
|
|
|
with FSizing do
|
|
if gsRowSizing = fGridState then begin
|
|
if FUseXORFeatures then begin
|
|
if (y-OffIni)<=0 then
|
|
y:= OffIni;
|
|
if y<>PrevOffset then begin
|
|
if PrevLine then
|
|
DrawXorHorzLine(PrevOffset);
|
|
DrawXorHorzLine(Y);
|
|
PrevLine:=True;
|
|
PrevOffset:=y;
|
|
end;
|
|
end else
|
|
ResizeRow(Index, y-OffIni);
|
|
HeaderSizing(false, Index, y-OffIni);
|
|
exit(true);
|
|
end else
|
|
if (fGridState=gsNormal) and (RowCount>FixedRows) and
|
|
((FlipX(X)<FGCache.FixedWidth) or
|
|
(FExtendedRowSizing and (FlipX(X)<FGCache.MaxClientXY.X))) and
|
|
(Y>FGCache.FixedHeight) then
|
|
begin
|
|
|
|
// find closest cell and cell boundaries
|
|
if Y>FGCache.GridHeight-1 then
|
|
Index := RowCount-1
|
|
else
|
|
OffsetToColRow(False, True, Y, Index, OffEnd{dummy});
|
|
ColRowToOffset(False, True, Index, OffIni, OffEnd);
|
|
|
|
// find out what cell boundary is closer to Y
|
|
if OffEnd>FGCache.ClientHeight then
|
|
Offset := FGCache.ClientHeight
|
|
else
|
|
if (OffEnd-Y)<(Y-OffIni) then
|
|
Offset := OffEnd
|
|
else begin
|
|
Offset := OffIni;
|
|
Dec(Index);
|
|
ColRowToOffset(False, True, Index, OffIni, OffEnd);
|
|
end;
|
|
|
|
// check if it's not fixed row and if cursor is close enough to
|
|
// selected boundary
|
|
if (Index>=FFixedRows)and(Abs(Offset-Y)<=varColRowBorderTolerance) then begin
|
|
// start resizing
|
|
if FCursorState<>gcsRowHeightChanging then begin
|
|
ChangeCursor(RowSizingCursor);
|
|
FCursorState := gcsRowHeightChanging;
|
|
PrevLine := False;
|
|
PrevOffset := -1;
|
|
end;
|
|
exit(true);
|
|
end
|
|
|
|
end;
|
|
|
|
if (FCursorState=gcsRowHeightChanging) then
|
|
RestoreCursor;
|
|
end;
|
|
|
|
procedure TCustomGrid.ScrollerDoScroll(Dir: TPoint);
|
|
var
|
|
OldTopLeft: TPoint;
|
|
begin
|
|
OldTopLeft := FTopLeft;
|
|
if ((Dir.X < 0) and (FTopLeft.X > FFixedCols)) or ((Dir.X > 0) and (FGCache.FullVisibleGrid.Right + FixedCols < ColCount)) then
|
|
Inc(FTopLeft.X, Dir.X);
|
|
if ((Dir.Y < 0) and (FTopLeft.Y > FFixedRows)) or ((Dir.Y > 0) and (FGCache.FullVisibleGrid.Bottom + FixedRows < RowCount)) then
|
|
Inc(FTopLeft.Y, Dir.Y);
|
|
if FTopleft <> OldTopLeft then begin
|
|
FMoveLast := Point(-1, -1);
|
|
doTopleftChange(False);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetScroller(Dir: TPoint);
|
|
begin
|
|
if (Dir.X = 0) and (Dir.Y = 0) then begin
|
|
FreeAndNil(FScroller);
|
|
end else begin
|
|
if not Assigned(FScroller) then
|
|
FScroller := TGridScroller.Create(@ScrollerDoScroll);
|
|
FScroller.Start(Dir);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.doColMoving(X, Y: Integer);
|
|
var
|
|
CurCell: TPoint;
|
|
R: TRect;
|
|
begin
|
|
CurCell:=MouseToCell(Point(X,Y));
|
|
|
|
with FGCache do begin
|
|
|
|
if (Abs(ClickMouse.X-X)>FDragDX) and (FCursorState<>gcsDragging) then begin
|
|
ChangeCursor(ColRowDraggingCursor);
|
|
FCursorState := gcsDragging;
|
|
ResetLastMove;
|
|
end;
|
|
|
|
if (FCursorState=gcsDragging) and
|
|
(CurCell.X>=FFixedCols) and
|
|
((CurCell.X<=ClickCell.X) or (CurCell.X>ClickCell.X)) and
|
|
(CurCell.X<>FMoveLast.X) then begin
|
|
|
|
R := CellRect(CurCell.X, CurCell.Y);
|
|
if CurCell.X<=ClickCell.X then
|
|
FMoveLast.Y := R.Left
|
|
else
|
|
FMoveLast.Y := R.Right;
|
|
|
|
FMoveLast.X := CurCell.X;
|
|
{$ifdef AlternativeMoveIndicator}
|
|
InvalidateRow(0);
|
|
{$else}
|
|
Invalidate;
|
|
{$endif}
|
|
end;
|
|
end;
|
|
|
|
if (X > FGCache.MaxClientXY.X) or (X > FGCache.ClientWidth + GetBorderWidth) then
|
|
SetScroller(Point(1, 0))
|
|
else if X < FGCache.FixedWidth then
|
|
SetScroller(Point(-1, 0))
|
|
else
|
|
SetScroller(Point(0, 0));
|
|
end;
|
|
|
|
procedure TCustomGrid.doRowMoving(X, Y: Integer);
|
|
var
|
|
CurCell: TPoint;
|
|
R: TRect;
|
|
begin
|
|
CurCell:=MouseToCell(Point(X,Y));
|
|
|
|
with FGCache do begin
|
|
|
|
if (FCursorState<>gcsDragging) and (Abs(ClickMouse.Y-Y)>FDragDX) then begin
|
|
ChangeCursor(ColRowDraggingCursor);
|
|
FCursorState := gcsDragging;
|
|
ResetLastMove;
|
|
end;
|
|
|
|
if (FCursorState=gcsDragging)and
|
|
(CurCell.Y>=FFixedRows) and
|
|
((CurCell.Y<=ClickCell.Y) or (CurCell.Y>ClickCell.Y))and
|
|
(CurCell.Y<>FMoveLast.Y) then begin
|
|
|
|
R:=CellRect(CurCell.X, CurCell.Y);
|
|
if CurCell.Y<=ClickCell.Y then
|
|
FMoveLast.X:=R.Top
|
|
else
|
|
FMoveLast.X:=R.Bottom;
|
|
FMoveLast.Y:=CurCell.Y;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
if (Y > FGCache.MaxClientXY.Y) or (Y > FGCache.ClientHeight + GetBorderWidth) then
|
|
SetScroller(Point(0, 1))
|
|
else if Y < FGCache.FixedHeight then
|
|
SetScroller(Point(0, -1))
|
|
else
|
|
SetScroller(Point(0, 0));
|
|
end;
|
|
|
|
|
|
function TCustomGrid.OffsetToColRow(IsCol, Physical: Boolean; Offset: Integer;
|
|
out Index, Rest: Integer): Boolean;
|
|
begin
|
|
Index:=0;
|
|
Rest:=0;
|
|
Result := False;
|
|
if IsCol and UseRightToLeftAlignment then
|
|
Offset := FlipX(Offset);
|
|
Offset := Offset - GetBorderWidth;
|
|
if Offset<0 then Exit; // Out of Range;
|
|
|
|
with FGCache do begin
|
|
if IsCol then begin
|
|
// begin to count Cols from 0 but ...
|
|
if Physical and (Offset>FixedWidth-1) then begin
|
|
Index := FTopLeft.X; // In scrolled view, then begin from FTopLeft col
|
|
if IsColumnIndexValid(Index) then begin
|
|
Offset:=Offset-FixedWidth+AccumWidth[Index];
|
|
if GetSmoothScroll(SB_Horz) then
|
|
Offset:=Offset+TLColOff;
|
|
end;
|
|
if not IsColumnIndexValid(Index) or (Offset>GridWidth-1) then begin
|
|
if AllowOutboundEvents then
|
|
Index := ColCount-1
|
|
else
|
|
Index := -1;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
while Offset > AccumWidth[Index]+GetColWidths(Index)-1 do begin
|
|
Inc(Index);
|
|
if not IsColumnIndexValid(Index) then begin
|
|
if AllowOutBoundEvents then
|
|
Index := ColCount-1
|
|
else
|
|
Index := -1;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
Rest:=Offset;
|
|
if Index<>0 then
|
|
Rest:=Offset-AccumWidth[Index];
|
|
|
|
end else begin
|
|
|
|
//DebugLn('TCustomGrid.OffsetToColRow ',DbgSName(Self),' Physical=',dbgs(Physical),' Offset=',dbgs(Offset),' FixedHeight=',dbgs(FixedHeight),' FTopLeft=',dbgs(FTopLeft),' RowCount=',dbgs(RowCount),' TLRowOff=',dbgs(TLRowOff));
|
|
if Physical and (Offset>FixedHeight-1) then begin
|
|
Index:=FTopLeft.Y;
|
|
if IsRowIndexValid(Index) then
|
|
Offset:=Offset-FixedHeight+AccumHeight[Index]+TLRowOff;
|
|
if not IsRowIndexValid(Index) or (Offset>GridHeight-1) then begin
|
|
if AllowOutboundEvents then
|
|
Index := RowCount-1
|
|
else
|
|
Index := -1;
|
|
Exit; // Out of Range
|
|
end;
|
|
end;
|
|
|
|
while Offset > AccumHeight[Index]+GetRowHeights(Index)-1 do
|
|
Inc(Index);
|
|
|
|
Rest:=Offset;
|
|
if Index<>0 then
|
|
Rest:=Offset-AccumHeight[Index];
|
|
|
|
end;
|
|
end;
|
|
result := True;
|
|
end;
|
|
|
|
{ ------------------------------------------------------------------------------
|
|
Example:
|
|
IsCol=true, Index:=100, TopLeft.x:=98, FixedCols:=1, all ColWidths:=20
|
|
Relative => StartPos := WidthfixedCols+WidthCol98+WidthCol99
|
|
not Relative = Absolute => StartPos := WidthCols(0..99) }
|
|
function TCustomGrid.ColRowToOffset(IsCol, Relative: Boolean; Index: Integer;
|
|
out StartPos, EndPos: Integer): Boolean;
|
|
var
|
|
Dim: Integer;
|
|
begin
|
|
Result:=false;
|
|
with FGCache do begin
|
|
if IsCol then begin
|
|
if not IsColumnIndexValid(Index) then
|
|
exit;
|
|
StartPos:=AccumWidth[index];
|
|
Dim:=GetColWidths(index);
|
|
end else begin
|
|
if not IsRowIndexValid(Index) then
|
|
exit;
|
|
StartPos:=AccumHeight[index];
|
|
Dim:= GetRowHeights(index);
|
|
end;
|
|
StartPos := StartPos + GetBorderWidth;
|
|
if not Relative then begin
|
|
EndPos:=StartPos + Dim;
|
|
Exit;
|
|
end;
|
|
if IsCol then begin
|
|
if IsColumnIndexVariable(Index) then begin
|
|
StartPos:=StartPos-AccumWidth[FTopLeft.X] + FixedWidth;
|
|
if GetSmoothScroll(SB_Horz) then
|
|
StartPos := StartPos - TLColOff;
|
|
end;
|
|
end else begin
|
|
if IsRowIndexVariable(Index) then begin
|
|
StartPos:=StartPos-AccumHeight[FTopLeft.Y] + FixedHeight;
|
|
if GetSmoothScroll(SB_Vert) then
|
|
StartPos := StartPos - TLRowOff;
|
|
end;
|
|
end;
|
|
if IsCol and UseRightToLeftAlignment then
|
|
begin
|
|
EndPos := FlipX(StartPos) + 1;
|
|
StartPos := EndPos - Dim;
|
|
end
|
|
else
|
|
EndPos:=StartPos + Dim;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TCustomGrid.ColumnIndexFromGridColumn(Column: Integer): Integer;
|
|
begin
|
|
if Columns.Enabled and (Column>=FirstGridColumn) then
|
|
result := Columns.RealIndex(Column - FirstGridColumn)
|
|
else
|
|
result := -1;
|
|
end;
|
|
|
|
function TCustomGrid.ColumnFromGridColumn(Column: Integer): TGridColumn;
|
|
var
|
|
ColIndex: Integer;
|
|
begin
|
|
ColIndex := ColumnIndexFromGridColumn(Column);
|
|
if ColIndex>=0 then
|
|
result := Columns[ColIndex]
|
|
else
|
|
result := nil;
|
|
end;
|
|
|
|
procedure TCustomGrid.ColumnsChanged(aColumn: TGridColumn);
|
|
var
|
|
aCol: Integer;
|
|
begin
|
|
if csDestroying in ComponentState then
|
|
exit;
|
|
|
|
if AColumn=nil then begin
|
|
if Columns.Enabled then begin
|
|
if FirstGridColumn + Columns.VisibleCount <> ColCount then
|
|
InternalSetColCount( FirstGridColumn + Columns.VisibleCount )
|
|
else
|
|
VisualChange;
|
|
end else
|
|
if not (csLoading in ComponentState) then
|
|
ColCount := FixedCols;
|
|
end else begin
|
|
aCol := Columns.IndexOf(AColumn);
|
|
if ACol>=0 then begin
|
|
VisualChange;
|
|
{
|
|
if aColumn.WidthChanged then
|
|
VisualChange
|
|
else
|
|
InvalidateCol(FixedCols + ACol);
|
|
}
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
function TCustomGrid.MouseToGridZone(X, Y: Integer): TGridZone;
|
|
var
|
|
aBorderWidth: Integer;
|
|
aCol, aRow: Longint;
|
|
begin
|
|
aBorderWidth := GetBorderWidth;
|
|
if FlipX(X)<FGCache.FixedWidth+aBorderWidth then begin
|
|
// in fixedwidth zone
|
|
if Y<FGcache.FixedHeight+aBorderWidth then
|
|
Result:= gzFixedCells
|
|
else begin
|
|
OffsetToColRow(False, True, Y, aRow, aCol);
|
|
if (aRow<0) or (RowCount<=FixedRows) then
|
|
Result := gzInvalid
|
|
else
|
|
Result := gzFixedRows;
|
|
end;
|
|
end
|
|
else if Y<FGCache.FixedHeight+aBorderWidth then begin
|
|
// if fixedheight zone
|
|
if FlipX(X)<FGCache.FixedWidth+aBorderWidth then
|
|
Result:=gzFixedCells
|
|
else begin
|
|
OffsetToColRow(True, True, X, aCol, aRow);
|
|
if (aCol<0) or (ColCount<=FixedCols) then
|
|
Result := gzInvalid
|
|
else
|
|
Result := gzFixedCols;
|
|
end;
|
|
end
|
|
else if not FixedGrid then begin
|
|
// in normal cell zone (though, might be outbounds)
|
|
MouseToCell(x, y, aCol, aRow);
|
|
if (aCol<0) or (aRow<0) then
|
|
result := gzInvalid
|
|
else
|
|
result := gzNormal;
|
|
end
|
|
else
|
|
result := gzInvalid;
|
|
end;
|
|
|
|
function TCustomGrid.CellToGridZone(aCol, aRow: Integer): TGridZone;
|
|
begin
|
|
if (aCol<0) or (aRow<0) then
|
|
Result := gzInvalid
|
|
else
|
|
if (aCol<FFixedCols) then
|
|
if aRow<FFixedRows then
|
|
Result:= gzFixedCells
|
|
else
|
|
Result:= gzFixedRows
|
|
else
|
|
if (aRow<FFixedRows) then
|
|
if aCol<FFixedCols then
|
|
Result:= gzFixedCells
|
|
else
|
|
Result:= gzFixedCols
|
|
else
|
|
Result := gzNormal;
|
|
end;
|
|
|
|
procedure TCustomGrid.DoOPExchangeColRow(IsColumn: Boolean; index, WithIndex: Integer);
|
|
var
|
|
aColRow: integer;
|
|
begin
|
|
|
|
if IsColumn and Columns.Enabled then begin
|
|
Columns.ExchangeColumn( ColumnIndexFromGridColumn(Index),
|
|
ColumnIndexFromGridColumn(WithIndex));
|
|
ColRowExchanged(IsColumn, index, WithIndex);
|
|
exit;
|
|
end;
|
|
// exchanges column widths or row heights
|
|
if IsColumn then
|
|
FCols.Exchange(index, WithIndex)
|
|
else
|
|
FRows.Exchange(index, WithIndex);
|
|
ColRowExchanged(IsColumn, index, WithIndex);
|
|
VisualChange;
|
|
|
|
// adjust editor bounds
|
|
if IsColumn then
|
|
aColRow := FCol
|
|
else
|
|
aColRow := FRow;
|
|
|
|
if Between(aColRow, Index, WithIndex) then begin
|
|
if aColRow=Index then
|
|
aColRow:=WithIndex
|
|
else
|
|
if aColRow=WithIndex then
|
|
aColRow:=Index;
|
|
if IsColumn then
|
|
AdjustEditorBounds(aColRow, FRow)
|
|
else
|
|
AdjustEditorBounds(FCol, aColRow);
|
|
end;
|
|
|
|
// adjust sort column
|
|
if IsColumn and (FSortColumn>=0) then begin
|
|
if Between(FSortColumn, Index, WithIndex) then begin
|
|
if FSortColumn=Index then
|
|
FSortColumn := WithIndex
|
|
else
|
|
if FSortColumn=WithIndex then
|
|
FSortColumn := Index;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.DoOPInsertColRow(IsColumn: boolean; index: integer);
|
|
var
|
|
NewCol,NewRow: Integer;
|
|
begin
|
|
if IsColumn and (RowCount = 0) then
|
|
Raise EGridException.Create(rsGridHasNoRows);
|
|
if not IsColumn then
|
|
begin
|
|
if (Columns.Enabled and (Columns.Count = 0)) or (not Columns.Enabled and (ColCount = 0)) then
|
|
Raise EGridException.Create(rsGridHasNoCols);
|
|
end;
|
|
|
|
if Index<0 then
|
|
Index:=0;
|
|
|
|
NewCol := Col;
|
|
NewRow := Row;
|
|
if IsColumn then begin
|
|
if Index>ColCount-1 then
|
|
Index := ColCount-1;
|
|
if Index<FixedCols then
|
|
inc(FFixedCols);
|
|
if columns.Enabled then begin
|
|
Columns.InsertColumn(ColumnIndexFromGridColumn(index));
|
|
ColRowInserted(true, index);
|
|
exit;
|
|
end else begin
|
|
FCols.Insert(Index, -1);
|
|
FGCache.AccumWidth.Insert(Index, -1);
|
|
end;
|
|
end else begin
|
|
Frows.Insert(Index, -1);
|
|
FGCache.AccumHeight.Insert(Index, -1);
|
|
if Index<FixedRows then
|
|
inc(FFixedRows);
|
|
end;
|
|
ColRowInserted(IsColumn, index);
|
|
VisualChange;
|
|
|
|
// adjust editor bounds
|
|
if IsColumn then begin
|
|
if NewCol<FixedCols then
|
|
NewCol := FixedCols
|
|
else
|
|
if Index<=NewCol then
|
|
Inc(NewCol);
|
|
end else begin
|
|
if NewRow<FixedRows then
|
|
NewRow := FixedRows
|
|
else
|
|
if Index<=NewRow then
|
|
Inc(NewRow);
|
|
end;
|
|
AdjustEditorBounds(NewCol, NewRow);
|
|
|
|
// adjust sorted column
|
|
if IsColumn and (FSortColumn>=Index) then
|
|
Inc(FSortColumn);
|
|
end;
|
|
|
|
procedure TCustomGrid.DoOPMoveColRow(IsColumn: Boolean; FromIndex,
|
|
ToIndex: Integer);
|
|
var
|
|
aColRow: Integer;
|
|
begin
|
|
if FromIndex=ToIndex then
|
|
begin
|
|
VisualChange;
|
|
exit;
|
|
end;
|
|
|
|
CheckIndex(IsColumn, FromIndex);
|
|
CheckIndex(IsColumn, ToIndex);
|
|
|
|
// move custom columns if they are not locked
|
|
if IsColumn and Columns.Enabled and (not(gfColumnsLocked in FGridFlags)) then begin
|
|
Columns.MoveColumn(ColumnIndexFromGridColumn(FromIndex),
|
|
ColumnIndexFromGridColumn(ToIndex));
|
|
// done
|
|
exit;
|
|
end;
|
|
|
|
// move grids content
|
|
if IsColumn then
|
|
FCols.Move(FromIndex, ToIndex)
|
|
else
|
|
FRows.Move(FromIndex, ToIndex);
|
|
ColRowMoved(IsColumn, FromIndex, ToIndex);
|
|
|
|
if not IsColumn or not Columns.Enabled then
|
|
VisualChange;
|
|
|
|
// adjust editor bounds
|
|
if IsColumn then
|
|
aColRow:=FCol
|
|
else
|
|
aColRow:=FRow;
|
|
if Between(aColRow, FromIndex, ToIndex) then begin
|
|
if aColRow=FromIndex then
|
|
aColRow := ToIndex
|
|
else
|
|
if FromIndex<aColRow then
|
|
aColRow := aColRow-1
|
|
else
|
|
aColRow := aColRow+1;
|
|
if IsColumn then
|
|
AdjustEditorBounds(aColRow, FRow)
|
|
else
|
|
AdjustEditorBounds(FCol, aColRow);
|
|
end;
|
|
|
|
// adjust sorted column
|
|
if IsColumn and (FSortColumn>=0) then
|
|
if Between(FSortColumn, FromIndex, ToIndex) then begin
|
|
if FSortColumn=FromIndex then
|
|
FSortColumn := ToIndex
|
|
else
|
|
if FromIndex<FSortColumn then
|
|
Dec(FSortColumn)
|
|
else
|
|
Inc(FSortColumn);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.DoOPDeleteColRow(IsColumn: Boolean; index: Integer);
|
|
|
|
procedure doDeleteColumn;
|
|
var
|
|
tmpIndex: Integer;
|
|
begin
|
|
CheckFixedCount(ColCount-1, RowCount, FFixedCols, FFixedRows);
|
|
CheckCount(ColCount-1, RowCount, false);
|
|
|
|
// before deleteing column hide editor
|
|
if EditorMode and (Index=Col) then
|
|
EditorMode:=False;
|
|
|
|
if Columns.Enabled then
|
|
tmpIndex := ColumnIndexFromGridColumn(Index);
|
|
|
|
if Index<FixedCols then begin
|
|
Dec(FFixedCols);
|
|
FTopLeft.x := FFixedCols;
|
|
end;
|
|
|
|
FCols.Delete(Index);
|
|
FGCache.AccumWidth.Delete(Index);
|
|
|
|
ColRowDeleted(True, Index);
|
|
|
|
if Columns.Enabled then
|
|
Columns.RemoveColumn(tmpIndex);
|
|
|
|
FixPosition(True, Index);
|
|
end;
|
|
|
|
procedure doDeleteRow;
|
|
begin
|
|
CheckFixedCount(ColCount, RowCount-1, FFixedCols, FFixedRows);
|
|
CheckCount(ColCount, RowCount-1, false);
|
|
// before deleteing row hide editor
|
|
if EditorMode and (Index=Row) then
|
|
EditorMode:=False;
|
|
if Index<FixedRows then begin
|
|
Dec(FFixedRows);
|
|
FTopLeft.y := FFixedRows;
|
|
end;
|
|
FRows.Delete(Index);
|
|
FGCache.AccumHeight.Delete(Index);
|
|
ColRowDeleted(False,Index);
|
|
FixPosition(False, Index);
|
|
|
|
If FRowAutoInserted And (Index=FixedRows+(RowCount-1)) Then
|
|
FRowAutoInserted := False;
|
|
end;
|
|
|
|
begin
|
|
CheckIndex(IsColumn,Index);
|
|
if IsColumn then begin
|
|
doDeleteColumn;
|
|
if FSortColumn=Index then
|
|
FSortColumn :=-1
|
|
else
|
|
if FSortColumn>Index then
|
|
Dec(FSortColumn);
|
|
end
|
|
else
|
|
doDeleteRow;
|
|
end;
|
|
|
|
function TCustomGrid.EditorByStyle(Style: TColumnButtonStyle): TWinControl;
|
|
begin
|
|
case Style of
|
|
cbsEllipsis:
|
|
Result := FButtonStringEditor;
|
|
cbsButton:
|
|
Result := FButtonEditor;
|
|
cbsPicklist:
|
|
Result := FPicklistEditor;
|
|
cbsAuto:
|
|
Result := FStringEditor;
|
|
else {cbsNone, cbsCheckboxColumn, cbsButtonColumn:}
|
|
Result := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
|
|
Y: Integer);
|
|
|
|
function CheckAutoEdit: boolean;
|
|
begin
|
|
result := FAutoEdit and not(csNoFocus in ControlStyle) and
|
|
EditingAllowed(FCol) and (FGCache.ClickCell.X=Col) and (FGCache.ClickCell.Y=Row);
|
|
if result then
|
|
GridFlags := GridFlags + [gfAutoEditPending];
|
|
end;
|
|
|
|
begin
|
|
inherited MouseDown(Button, Shift, X, Y);
|
|
|
|
if (csDesigning in componentState) or not MouseButtonAllowed(Button) then
|
|
Exit;
|
|
|
|
{$IfDef dbgGrid}DebugLnEnter('MouseDown %s INIT',[dbgsName(self)]); {$Endif}
|
|
|
|
FIgnoreClick := True;
|
|
|
|
{$IFDEF dbgGrid}
|
|
DebugLn('Mouse was in ', dbgs(FGCache.HotGridZone));
|
|
{$ENDIF}
|
|
|
|
if not Focused and not(csNoFocus in ControlStyle) then begin
|
|
if CanFocus then
|
|
SetFocus;
|
|
if not Focused then begin
|
|
{$ifDef dbgGrid} DebugLnExit('TCustomGrid.MouseDown EXIT: Focus not allowed'); {$Endif}
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
CacheMouseDown(X,Y);
|
|
|
|
case FGCache.HotGridZone of
|
|
|
|
gzFixedCells:
|
|
begin
|
|
if (goColSizing in Options) and (goFixedColSizing in Options) and
|
|
(FCursorState=gcsColWidthChanging) then
|
|
fGridState:= gsColSizing
|
|
else begin
|
|
FGridState := gsHeaderClicking;
|
|
if ((goHeaderPushedLook in Options) and
|
|
(FGCache.HotGridZone in FHeaderPushZones)) then
|
|
DoPushCell;
|
|
end;
|
|
end;
|
|
|
|
gzFixedCols:
|
|
begin
|
|
if (goColSizing in Options) and (FCursorState=gcsColWidthChanging) then begin
|
|
fGridState:= gsColSizing;
|
|
FGCache.OldMaxTopLeft := FGCache.MaxTopLeft;
|
|
end
|
|
else begin
|
|
// ColMoving or Clicking
|
|
if fGridState<>gsColMoving then begin
|
|
fGridState:=gsColMoving;
|
|
ResetLastMove;
|
|
end;
|
|
|
|
if ((goHeaderPushedLook in Options) and
|
|
(FGCache.HotGridZone in FHeaderPushZones)) then
|
|
DoPushCell;
|
|
end;
|
|
end;
|
|
|
|
gzFixedRows:
|
|
if (goRowSizing in Options) and (FCursorState=gcsRowHeightChanging) then
|
|
fGridState:= gsRowSizing
|
|
else begin
|
|
// RowMoving or Clicking
|
|
fGridState:=gsRowMoving;
|
|
ResetLastMove;
|
|
if ((goHeaderPushedLook in Options) and
|
|
(FGCache.HotGridZone in FHeaderPushZones)) then
|
|
DoPushCell;
|
|
end;
|
|
|
|
gzNormal:
|
|
begin
|
|
LockEditor;
|
|
FIgnoreClick := False;
|
|
UnlockEditor;
|
|
if IsMouseOverCellButton(X, Y) then begin
|
|
StartPushCell;
|
|
Exit;
|
|
end else
|
|
if FExtendedColSizing and
|
|
(FCursorState=gcsColWidthChanging) and
|
|
(goColSizing in Options) then begin
|
|
// extended column sizing
|
|
fGridState:= gsColSizing;
|
|
|
|
end
|
|
else if not FixedGrid then begin
|
|
// normal selecting
|
|
fGridState:=gsSelecting;
|
|
|
|
if not EditingAllowed(FCol) or
|
|
(ExtendedSelect and not EditorAlwaysShown) then begin
|
|
|
|
if ssShift in Shift then
|
|
SelectActive:=(goRangeSelect in Options)
|
|
else begin
|
|
if (goRangeSelect in Options) and (FRangeSelectMode = rsmMulti)
|
|
then begin
|
|
if (MULTISEL_MODIFIER in Shift) then
|
|
AddSelectedRange
|
|
else begin
|
|
ClearSelections;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
// shift is not pressed any more cancel SelectActive if necessary
|
|
if SelectActive then
|
|
CancelSelection;
|
|
|
|
if not SelectActive then begin
|
|
CheckAutoEdit;
|
|
GridFlags := GridFlags + [gfNeedsSelectActive];
|
|
FPivot:=FGCache.ClickCell;
|
|
|
|
end;
|
|
end;
|
|
|
|
end else if CheckAutoEDit then begin
|
|
{$ifDef dbgGrid} DebugLnExit('MouseDown (autoedit) EXIT'); {$Endif}
|
|
Exit;
|
|
end;
|
|
|
|
include(fGridFlags, gfEditingDone);
|
|
try
|
|
if not MoveExtend(False, FGCache.ClickCell.X, FGCache.ClickCell.Y, False) then begin
|
|
if EditorAlwaysShown then begin
|
|
SelectEditor;
|
|
EditorShow(true);
|
|
end;
|
|
MoveSelection;
|
|
end else
|
|
FGridState:=gsSelecting;
|
|
finally
|
|
exclude(fGridFlags, gfEditingDone);
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
{$ifDef dbgGrid}DebugLnExit('MouseDown END'); {$Endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.MouseMove(Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
p: TPoint;
|
|
obe: boolean; // stored "AllowOutboundEvents"
|
|
begin
|
|
inherited MouseMove(Shift, X, Y);
|
|
|
|
if Dragging then
|
|
exit;
|
|
|
|
HeadersMouseMove(X,Y);
|
|
|
|
case FGridState of
|
|
gsHeaderClicking, gsButtonColumnClicking:
|
|
;
|
|
gsSelecting:
|
|
if not FixedGrid and (not EditingAllowed(-1) or
|
|
(ExtendedSelect and not EditorAlwaysShown)) then begin
|
|
P:=MouseToLogcell(Point(X,Y));
|
|
if gfNeedsSelectActive in GridFlags then
|
|
SelectActive := (P.x<>FPivot.x)or(P.y<>FPivot.y);
|
|
MoveExtend(false, P.X, P.Y, false);
|
|
end;
|
|
gsColMoving:
|
|
if goColMoving in Options then
|
|
doColMoving(X,Y);
|
|
gsRowMoving:
|
|
if goRowMoving in Options then
|
|
doRowMoving(X,Y);
|
|
|
|
else
|
|
begin
|
|
if goColSizing in Options then
|
|
doColSizing(X,Y);
|
|
|
|
if goRowSizing in Options then
|
|
doRowSizing(X,Y);
|
|
|
|
obe := AllowOutboundEvents;
|
|
AllowOutboundEvents := false;
|
|
try
|
|
p := MouseCoord(X, Y);
|
|
finally
|
|
AllowOutboundEvents := obe;
|
|
end;
|
|
|
|
// if we are not over a cell
|
|
if p.X < 0 then
|
|
begin
|
|
// empty hints
|
|
Application.Hint := '';
|
|
Hint := '';
|
|
// if FCellHintPriority = chpAll, restore default hint
|
|
if ShowHint and (FCellHintPriority = chpAll) then
|
|
begin
|
|
Hint := FSavedHint;
|
|
Application.Hint := GetLongHint(FSavedHint);
|
|
end;
|
|
end;
|
|
|
|
with FGCache do
|
|
if (MouseCell.X <> p.X) or (MouseCell.Y <> p.Y) then begin
|
|
Application.CancelHint;
|
|
ShowCellHintWindow(Point(X,Y));
|
|
MouseCell := p;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
|
|
Y: Integer);
|
|
var
|
|
Cur: TPoint;
|
|
Gz: TGridZone;
|
|
|
|
function IsValidCellClick: boolean;
|
|
begin
|
|
result := (Cur.X=FGCache.ClickCell.X) and (Cur.Y=FGCache.ClickCell.Y) and (gz<>gzInvalid);
|
|
end;
|
|
|
|
procedure DoAutoEdit;
|
|
begin
|
|
if (gfAutoEditPending in GridFlags){ and not (ssDouble in Shift)} then begin
|
|
SelectEditor;
|
|
EditorShow(True);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
inherited MouseUp(Button, Shift, X, Y);
|
|
{$IfDef dbgGrid}DebugLn('MouseUP INIT');{$Endif}
|
|
|
|
Cur:=MouseToCell(Point(x,y));
|
|
Gz :=CellToGridZone(cur.x, cur.y);
|
|
|
|
case fGridState of
|
|
|
|
gsHeaderClicking, gsButtonColumnClicking:
|
|
if IsValidCellClick then begin
|
|
if fGridState=gsHeaderClicking then
|
|
HeaderClick(True, FGCache.ClickCell.X)
|
|
else
|
|
if Assigned(OnEditButtonClick) or Assigned(OnButtonClick) then
|
|
DoEditButtonClick(Cur.X, Cur.Y);
|
|
end;
|
|
|
|
gsNormal:
|
|
if not FixedGrid and IsValidCellClick then begin
|
|
doAutoEdit;
|
|
CellClick(cur.x, cur.y, Button);
|
|
end;
|
|
|
|
gsSelecting:
|
|
begin
|
|
if SelectActive then
|
|
MoveExtend(False, Cur.x, Cur.y, False)
|
|
else begin
|
|
doAutoEdit;
|
|
CellClick(cur.x, cur.y, Button);
|
|
end;
|
|
end;
|
|
|
|
gsColMoving:
|
|
begin
|
|
//DebugLn('Move Col From ',Fsplitter.x,' to ', FMoveLast.x);
|
|
RestoreCursor;
|
|
FreeAndNil(FScroller);
|
|
|
|
if FMoveLast.X>=0 then
|
|
DoOPMoveColRow(True, FGCache.ClickCell.X, FMoveLast.X)
|
|
else
|
|
if Cur.X=FGCache.ClickCell.X then
|
|
HeaderClick(True, FGCache.ClickCell.X)
|
|
end;
|
|
|
|
gsRowMoving:
|
|
begin
|
|
//DebugLn('Move Row From ',Fsplitter.Y,' to ', FMoveLast.Y);
|
|
RestoreCursor;
|
|
|
|
if FMoveLast.Y>=0 then
|
|
DoOPMoveColRow(False, FGCache.ClickCell.Y, FMoveLast.Y)
|
|
else
|
|
if Cur.Y=FGCache.ClickCell.Y then
|
|
HeaderClick(False, FGCache.ClickCell.Y);
|
|
end;
|
|
|
|
gsColSizing:
|
|
if gfSizingStarted in FGridFlags then
|
|
with FSizing do begin
|
|
if FUseXORFeatures then begin
|
|
if PrevLine then
|
|
DrawXorVertLine(PrevOffset);
|
|
PrevLine := False;
|
|
PrevOffset := -1;
|
|
end;
|
|
if UseRightToLeftAlignment then
|
|
ResizeColumn(Index, OffEnd - X + DeltaOff)
|
|
else
|
|
ResizeColumn(Index, X - OffIni + DeltaOff);
|
|
FixScroll;
|
|
HeaderSized(True, Index);
|
|
end;
|
|
|
|
gsRowSizing:
|
|
with FSizing do begin
|
|
if FUseXORFeatures then begin
|
|
if PrevLine then
|
|
DrawXorHorzLine(PrevOffset);
|
|
PrevLine := False;
|
|
PrevOffset := -1;
|
|
end;
|
|
ResizeRow(Index, Y - OffIni);
|
|
HeaderSized(False, Index);
|
|
end;
|
|
|
|
end;
|
|
|
|
GridFlags := GridFlags - [gfNeedsSelectActive, gfSizingStarted, gfAutoEditPending];
|
|
|
|
if IsPushCellActive() then begin
|
|
ResetPushedCell;
|
|
end;
|
|
|
|
if (FMoveLast.X>=0) or (FMoveLast.Y>=0) then begin
|
|
{$ifdef AlternativeMoveIndicator}
|
|
begin
|
|
if FMoveLast.X>=0 then InvalidateRow(0);
|
|
if FMoveLast.Y>=0 then InvalidateCol(0);
|
|
end;
|
|
{$endif}
|
|
if not (fGridState in [gsColMoving,gsRowMoving]) then
|
|
RestoreCursor;
|
|
end;
|
|
|
|
FGCache.ClickCell := point(-1, -1);
|
|
|
|
fGridState:=gsNormal;
|
|
{$IfDef dbgGrid}DebugLn('MouseUP END RND=', FloatToStr(Random));{$Endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.DblClick;
|
|
var
|
|
OldWidth: Integer;
|
|
begin
|
|
{$IfDef dbgGrid}DebugLn('DoubleClick INIT');{$Endif}
|
|
SelectActive:=False;
|
|
fGridState:=gsNormal;
|
|
if (goColSizing in Options) and (FCursorState=gcsColWidthChanging) then begin
|
|
if (goDblClickAutoSize in Options) then begin
|
|
OldWidth := ColWidths[FSizing.Index];
|
|
AutoAdjustColumn( FSizing.Index );
|
|
if OldWidth<>ColWidths[FSizing.Index] then begin
|
|
RestoreCursor;
|
|
HeaderSized(True, FSizing.Index);
|
|
end;
|
|
end {else
|
|
DebugLn('Got Doubleclick on Col Resizing: AutoAdjust?');}
|
|
end else
|
|
if (goDblClickAutoSize in Options) and
|
|
(goRowSizing in Options) and
|
|
(FCursorState=gcsRowHeightChanging) then begin
|
|
{
|
|
DebugLn('Got DoubleClick on Row Resizing: AutoAdjust?');
|
|
}
|
|
end
|
|
else
|
|
Inherited DblClick;
|
|
{$IfDef dbgGrid}DebugLn('DoubleClick END');{$Endif}
|
|
end;
|
|
|
|
function TCustomGrid.DefaultColWidthIsStored: Boolean;
|
|
begin
|
|
Result := FDefColWidth>=0;
|
|
end;
|
|
|
|
function TCustomGrid.DefaultRowHeightIsStored: Boolean;
|
|
begin
|
|
Result := FDefRowHeight>=0;
|
|
end;
|
|
|
|
procedure TCustomGrid.DefineProperties(Filer: TFiler);
|
|
|
|
function SonRowsIguales(aGrid: TCustomGrid): boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
result := aGrid.RowCount = RowCount;
|
|
if Result then
|
|
for i:=0 to RowCount-1 do
|
|
if aGrid.RowHeights[i]<>RowHeights[i] then begin
|
|
result := false;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function SonColsIguales(aGrid: TCustomGrid): boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
result := aGrid.ColCount = ColCount;
|
|
if Result then
|
|
for i:=0 to ColCount-1 do
|
|
if aGrid.ColWidths[i]<>ColWidths[i] then begin
|
|
result := false;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function SonDefault(IsColumn: Boolean; L1: TIntegerList): boolean;
|
|
var
|
|
i: Integer;
|
|
DefValue: Integer;
|
|
begin
|
|
Result := True;
|
|
if IsColumn then DefValue := DefaultColWidth
|
|
else DefValue := DefaultRowHeight;
|
|
for i:=0 to L1.Count-1 do begin
|
|
Result := (L1[i] = DefValue) or (L1[i] < 0);
|
|
if not Result then
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function NeedWidths: boolean;
|
|
begin
|
|
if Filer.Ancestor is TCustomGrid then
|
|
Result := not SonColsIguales(TCustomGrid(Filer.Ancestor))
|
|
else
|
|
Result := not SonDefault(True, FCols);
|
|
//result := Result and not AutoFillColumns;
|
|
end;
|
|
|
|
function NeedHeights: boolean;
|
|
begin
|
|
if Filer.Ancestor is TCustomGrid then
|
|
Result := not SonRowsIguales(TCustomGrid(Filer.Ancestor))
|
|
else
|
|
Result := not SonDefault(false, FRows);
|
|
end;
|
|
|
|
function HasColumns: boolean;
|
|
var
|
|
C: TGridColumns;
|
|
begin
|
|
if Filer.Ancestor is TCustomGrid then
|
|
C := TCustomGrid(Filer.Ancestor).Columns
|
|
else
|
|
C := Columns;
|
|
if C<>nil then
|
|
result := not C.IsDefault
|
|
else
|
|
result := false;
|
|
end;
|
|
|
|
begin
|
|
inherited DefineProperties(Filer);
|
|
with Filer do begin
|
|
//DefineProperty('Columns', @ReadColumns, @WriteColumns, HasColumns);
|
|
DefineProperty('ColWidths', @ReadColWidths, @WriteColWidths, NeedWidths);
|
|
DefineProperty('RowHeights', @ReadRowHeights, @WriteRowHeights, NeedHeights);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.DestroyHandle;
|
|
begin
|
|
inherited DestroyHandle;
|
|
editorGetValue;
|
|
end;
|
|
|
|
function TCustomGrid.DialogChar(var Message: TLMKey): boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to Columns.Count-1 do
|
|
if Columns[i].Visible and (Columns[i].Title.PrefixOption<>poNone) then
|
|
if IsAccel(Message.CharCode, Columns[i].Title.Caption) then begin
|
|
result := true;
|
|
HeaderClick(True, GridColumnFromColumnIndex(i));
|
|
exit;
|
|
end;
|
|
result := inherited DialogChar(Message);
|
|
end;
|
|
|
|
function TCustomGrid.DoCompareCells(Acol, ARow, Bcol, BRow: Integer): Integer;
|
|
begin
|
|
result := 0;
|
|
if Assigned(OnCompareCells) then
|
|
OnCompareCells(Self, ACol, ARow, BCol, BRow, Result);
|
|
end;
|
|
|
|
procedure TCustomGrid.DoCopyToClipboard;
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomGrid.DoCutToClipboard;
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomGrid.DoEditButtonClick(const ACol, ARow: Integer);
|
|
var
|
|
OldCol,OldRow: Integer;
|
|
begin
|
|
OldCol:=FCol;
|
|
OldRow:=FRow;
|
|
try
|
|
FCol:=ACol;
|
|
FRow:=ARow;
|
|
if Assigned(OnEditButtonClick) then
|
|
OnEditButtonClick(Self);
|
|
if Assigned(OnButtonClick) then
|
|
OnButtonClick(Self, ACol, ARow);
|
|
finally
|
|
if (FCol=ACol) and (FRow=ARow) then
|
|
begin
|
|
// didn't change FRow or FCol, restore old index.
|
|
FCol:=OldCol;
|
|
FRow:=OldRow;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.DoEditorHide;
|
|
var
|
|
ParentForm: TCustomForm;
|
|
begin
|
|
{$ifdef dbgGrid}DebugLnEnter('grid.DoEditorHide [',Editor.ClassName,'] INIT');{$endif}
|
|
if gfEditingDone in FGridFlags then begin
|
|
ParentForm := GetParentForm(Self);
|
|
if Self.CanFocus then
|
|
ParentForm.ActiveControl := self;
|
|
end;
|
|
Editor.Visible:=False;
|
|
{$ifdef dbgGrid}DebugLnExit('grid.DoEditorHide [',Editor.ClassName,'] END');{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.DoEditorShow;
|
|
var
|
|
ParentChanged: Boolean;
|
|
Column: TGridColumn;
|
|
begin
|
|
{$ifdef dbgGrid}DebugLnEnter('grid.DoEditorShow [',Editor.ClassName,'] INIT');{$endif}
|
|
ScrollToCell(FCol,FRow, True);
|
|
// Under carbon, Editor.Parent:=nil destroy Editor handle, but not immediately
|
|
// as in this case where keyboard event on editor is being handled.
|
|
// After Editor.Visible:=true, a new handle is allocated but it's got overwritten
|
|
// once the delayed destroying of previous handle happens, the result is a stalled
|
|
// unparented editor ....
|
|
ParentChanged := (Editor.Parent<>Self);
|
|
if ParentChanged then
|
|
Editor.Parent := nil;
|
|
EditorSetValue;
|
|
if ParentChanged then
|
|
Editor.Parent:=Self;
|
|
if (FEditor = FStringEditor) or (FEditor = FButtonStringEditor) then
|
|
begin
|
|
Column:=ColumnFromGridColumn(FCol);
|
|
if Column<>nil then
|
|
FStringEditor.Alignment:=Column.Alignment
|
|
else
|
|
FStringEditor.Alignment:=taLeftJustify;
|
|
end;
|
|
TWinControlAccess(FEditor).ParentColor := (goEditorParentColor in Options2);
|
|
TWinControlAccess(FEditor).ParentFont := (goEditorParentFont in Options2);
|
|
if (FEditor is TCompositeCellEditor) then
|
|
begin
|
|
TWinControlAccess(TCompositeCellEditor(FEditor).ActiveControl).ParentColor := (goEditorParentColor in Options2);
|
|
TWinControlAccess(TCompositeCellEditor(FEditor).ActiveControl).ParentFont := (goEditorParentFont in Options2);
|
|
end;
|
|
|
|
Editor.Visible:=True;
|
|
if Focused and Editor.CanFocus then
|
|
Editor.SetFocus;
|
|
InvalidateCell(FCol,FRow,True);
|
|
{$ifdef dbgGrid}DebugLnExit('grid.DoEditorShow [',Editor.ClassName,'] END');{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
|
|
const AXProportion, AYProportion: Double);
|
|
var
|
|
i: Integer;
|
|
C: TGridColumn;
|
|
begin
|
|
inherited;
|
|
|
|
if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
for i := Columns.Count - 1 downto 0 do
|
|
begin
|
|
C := Columns.Items[i];
|
|
C.MaxSize := Round(C.MaxSize * AXProportion);
|
|
C.MinSize := Round(C.MinSize * AXProportion);
|
|
if C.IsWidthStored then
|
|
C.Width := Round(C.Width * AXProportion);
|
|
end;
|
|
|
|
for i := FRows.Count - 1 downto 0 do
|
|
if FRows[i]>=0 then
|
|
FRows[i] := Round(FRows[i] * AYProportion);
|
|
|
|
for i := FCols.Count - 1 downto 0 do
|
|
if FCols[i]>=0 then
|
|
FCols[i] := Round(FCols[i] * AXProportion);
|
|
|
|
if DefaultColWidthIsStored then
|
|
DefaultColWidth := Round(DefaultColWidth * AXProportion)
|
|
else
|
|
FRealizedDefColWidth := 0;
|
|
if DefaultRowHeightIsStored then
|
|
DefaultRowHeight := Round(DefaultRowHeight * AYProportion)
|
|
else
|
|
FRealizedDefRowHeight := 0;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.DoPasteFromClipboard;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TCustomGrid.DoPrepareCanvas(aCol,aRow:Integer; aState: TGridDrawState);
|
|
begin
|
|
if Assigned(OnPrepareCanvas) then
|
|
OnPrepareCanvas(Self, aCol, aRow, aState);
|
|
end;
|
|
|
|
procedure TCustomGrid.DoOnResize;
|
|
begin
|
|
inherited DoOnResize;
|
|
if FUpdateCount=0 then
|
|
TWSCustomGridClass(WidgetSetClass).Invalidate(Self);
|
|
end;
|
|
|
|
procedure TCustomGrid.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
|
|
begin
|
|
FLastWidth := ClientWidth;
|
|
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);
|
|
end;
|
|
|
|
function TCustomGrid.DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean;
|
|
begin
|
|
Result := inherited DoUTF8KeyPress(UTF8Key);
|
|
if EditingAllowed(FCol) and (not result) and (Length(UTF8Key)>1) then begin
|
|
EditorShowChar(UTF8Key);
|
|
UTF8Key := '';
|
|
Result := true
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.FlipRect(ARect: TRect): TRect;
|
|
begin
|
|
Result := BidiFlipRect(ARect, GCache.ClientRect, UseRightToLeftAlignment);
|
|
end;
|
|
|
|
function TCustomGrid.FlipPoint(P: TPoint): TPoint;
|
|
begin
|
|
Result := BidiFlipPoint(P, GCache.ClientRect, UseRightToLeftAlignment);
|
|
end;
|
|
|
|
function TCustomGrid.FlipX(X: Integer): Integer;
|
|
begin
|
|
Result := BidiFlipX(X, GCache.ClientRect, UseRightToLeftAlignment);
|
|
end;
|
|
|
|
function TCustomGrid.IsMouseOverCellButton(X, Y: Integer): boolean;
|
|
var
|
|
oldAOE: Boolean;
|
|
P: TPoint;
|
|
begin
|
|
oldAOE := AllowOutboundEvents;
|
|
AllowOutboundEvents := false;
|
|
P := MouseToCell(Point(X,Y));
|
|
AllowOutBoundEvents := OldAOE;
|
|
result := IsCellButtonColumn(P);
|
|
end;
|
|
|
|
procedure TCustomGrid.DoExit;
|
|
begin
|
|
if not (csDestroying in ComponentState) then begin
|
|
{$IfDef dbgGrid}DebugLnEnter('DoExit - INIT');{$Endif}
|
|
if FEditorShowing then begin
|
|
{$IfDef dbgGrid}DebugLn('DoExit - EditorShowing');{$Endif}
|
|
end else begin
|
|
{$IfDef dbgGrid}DebugLn('DoExit - Ext');{$Endif}
|
|
if not EditorAlwaysShown then
|
|
InvalidateFocused;
|
|
ResetEditor;
|
|
if FgridState=gsSelecting then begin
|
|
if SelectActive then
|
|
FSelectActive := False;
|
|
FGridState := gsNormal;
|
|
end;
|
|
end;
|
|
end;
|
|
inherited DoExit;
|
|
{$IfDef dbgGrid}DebugLnExit('DoExit - END');{$Endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.DoEnter;
|
|
begin
|
|
{$IfDef dbgGrid}DebugLnEnter('DoEnter %s INIT',[dbgsname(self)]);{$Endif}
|
|
inherited DoEnter;
|
|
if EditorLocked then begin
|
|
{$IfDef dbgGrid}DebugLn('DoEnter - EditorLocked');{$Endif}
|
|
end else begin
|
|
{$IfDef dbgGrid}DebugLn('DoEnter - Ext');{$Endif}
|
|
if EditorAlwaysShown then begin
|
|
// try to show editor only if focused cell is visible area
|
|
// so a mouse click would use click coords to show up
|
|
if IsCellVisible(Col,Row) then begin
|
|
SelectEditor;
|
|
if Feditor<>nil then
|
|
EditorShow(true);
|
|
end else begin
|
|
{$IfDef dbgGrid}DebugLn('DoEnter - Ext - Cell was not visible');{$Endif}
|
|
end;
|
|
end else
|
|
InvalidateFocused;
|
|
end;
|
|
{$IfDef dbgGrid}DebugLnExit('DoEnter - END');{$Endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.DoLoadColumn(Sender: TCustomGrid; aColumn: TGridColumn;
|
|
aColIndex: Integer; aCfg: TXMLConfig; aVersion: Integer; aPath: string);
|
|
begin
|
|
if Assigned(FOnLoadColumn) then
|
|
FOnLoadColumn(Self, aColumn, aColIndex, aCfg, aVersion, aPath);
|
|
end;
|
|
|
|
procedure TCustomGrid.DoSaveColumn(Sender: TCustomGrid; aColumn: TGridColumn;
|
|
aColIndex: Integer; aCfg: TXMLConfig; aVersion: Integer; aPath: string);
|
|
begin
|
|
if Assigned(FOnSaveColumn) then
|
|
FOnSaveColumn(Self, aColumn, aColIndex, aCfg, aVersion, aPath);
|
|
end;
|
|
|
|
function TCustomGrid.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
|
|
MousePos: TPoint): Boolean;
|
|
begin
|
|
if FMouseWheelOption=mwCursor then
|
|
FSelectActive := false;
|
|
Result:=inherited DoMouseWheel(Shift, WheelDelta, MousePos);
|
|
end;
|
|
|
|
function TCustomGrid.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint
|
|
): Boolean;
|
|
begin
|
|
{$ifdef dbgScroll}DebugLn('doMouseWheelDown INIT');{$endif}
|
|
Result:=inherited DoMouseWheelDown(Shift, MousePos);
|
|
if not Result then begin
|
|
GridMouseWheel(Shift, 1);
|
|
Result := True; // handled, no further scrolling by the widgetset
|
|
end;
|
|
{$ifdef dbgScroll}DebugLn('doMouseWheelDown END');{$endif}
|
|
end;
|
|
|
|
function TCustomGrid.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint
|
|
): Boolean;
|
|
begin
|
|
{$ifdef dbgScroll}DebugLn('doMouseWheelUP INIT');{$endif}
|
|
Result:=inherited DoMouseWheelUp(Shift, MousePos);
|
|
if not Result then begin
|
|
GridMouseWheel(Shift, -1);
|
|
Result := True; // handled, no further scrolling by the widgetset
|
|
end;
|
|
{$ifdef dbgScroll}DebugLn('doMouseWheelUP END');{$endif}
|
|
end;
|
|
|
|
function TCustomGrid.DoMouseWheelLeft(Shift: TShiftState; MousePos: TPoint
|
|
): Boolean;
|
|
begin
|
|
{$ifdef dbgScroll}DebugLn('doMouseWheelLEFT INIT');{$endif}
|
|
Result:=inherited DoMouseWheelLeft(Shift, MousePos);
|
|
if not Result then begin
|
|
GridMouseWheel([ssCtrl], -1);
|
|
Result := True; // handled, no further scrolling by the widgetset
|
|
end;
|
|
{$ifdef dbgScroll}DebugLn('doMouseWheelLEFT END');{$endif}
|
|
end;
|
|
|
|
function TCustomGrid.DoMouseWheelRight(Shift: TShiftState; MousePos: TPoint
|
|
): Boolean;
|
|
begin
|
|
{$ifdef dbgScroll}DebugLn('doMouseWheelRIGHT INIT');{$endif}
|
|
Result:=inherited DoMouseWheelRight(Shift, MousePos);
|
|
if not Result then begin
|
|
GridMouseWheel([ssCtrl], 1);
|
|
Result := True; // handled, no further scrolling by the widgetset
|
|
end;
|
|
{$ifdef dbgScroll}DebugLn('doMouseWheelRIGHT END');{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.DoOnChangeBounds;
|
|
var
|
|
OldTopLeft: TPoint;
|
|
OldColOff, OldRowOff: Integer;
|
|
begin
|
|
inherited DoOnChangeBounds;
|
|
|
|
if FUpdateCount=0 then
|
|
begin
|
|
OldTopLeft := fTopLeft;
|
|
OldColOff := FGCache.TLColOff;
|
|
OldRowOff := FGCache.TLRowOff;
|
|
UpdateSizes;
|
|
if (OldTopLeft.X<>FTopLeft.X) or (OldTopLeft.Y<>FTopLeft.Y)
|
|
or (OldColOff<>FGCache.TLColOff) or (OldRowOff<>FGCache.TLRowOff) then // reduce unnecessary repaints
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.KeyDown(var Key: Word; Shift: TShiftState);
|
|
var
|
|
Sh, PreserveRowAutoInserted: Boolean;
|
|
R: TRect;
|
|
Relaxed: Boolean;
|
|
DeltaCol,DeltaRow: Integer;
|
|
|
|
procedure MoveSel(Rel: Boolean; aCol,aRow: Integer);
|
|
begin
|
|
// Do not reset Offset in keyboard Events - see issue #29420
|
|
//FGCache.TLColOff:=0;
|
|
//FGCache.TLRowOff:=0;
|
|
SelectActive:=Sh;
|
|
Include(FGridFlags, gfEditingDone);
|
|
if MoveNextSelectable(Rel, aCol, aRow) then
|
|
Click;
|
|
Exclude(FGridFlags, gfEditingDone);
|
|
Key := 0; { Flag key as handled, even if selected cell did not move }
|
|
end;
|
|
|
|
procedure TabCheckEditorKey;
|
|
begin
|
|
if FEditorKey then begin
|
|
{$IFDEF dbggrid}
|
|
DebugLn('Got TAB, shift=',dbgs(sh));
|
|
{$endif}
|
|
if sh then
|
|
GridFlags := GridFlags + [gfRevEditorTab]
|
|
else
|
|
GridFlags := GridFlags + [gfEditorTab];
|
|
end;
|
|
end;
|
|
|
|
const
|
|
cBidiMove: array[Boolean] of Integer = (1, -1);
|
|
begin
|
|
{$ifdef dbgGrid}DebugLn('Grid.KeyDown INIT Key=',IntToStr(Key));{$endif}
|
|
inherited KeyDown(Key, Shift);
|
|
//Don't touch FRowAutoInserted flag if user presses only Ctrl,Shift,Altor Meta/Win key
|
|
PreserveRowAutoInserted := (Key in [VK_SHIFT,VK_CONTROL,VK_LWIN,VK_RWIN,VK_MENU]);
|
|
|
|
//if not FGCache.ValidGrid then Exit;
|
|
if not CanGridAcceptKey(Key, Shift) then
|
|
Key:=0; // Allow CanGridAcceptKey to override Key behaviour
|
|
Sh:=(ssShift in Shift);
|
|
Relaxed := not (goRowSelect in Options) or (goRelaxedRowSelect in Options);
|
|
|
|
case Key of
|
|
VK_TAB:
|
|
if goTabs in Options then begin
|
|
if GetDeltaMoveNext(Sh, DeltaCol,DeltaRow,FTabAdvance) then begin
|
|
Sh := False;
|
|
MoveSel(True, DeltaCol, DeltaRow);
|
|
PreserveRowAutoInserted := True;
|
|
Key:=0;
|
|
end else if (goAutoAddRows in Options) and (DeltaRow = 1) then begin
|
|
//prevent selecting multiple cells when user presses Shift
|
|
Sh := False;
|
|
if (goAutoAddRowsSkipContentCheck in Options) or (not IsEmptyRow(Row)) then MoveSel(True, DeltaCol, DeltaRow);
|
|
Key := 0;
|
|
PreserveRowAutoInserted := True;
|
|
end else
|
|
if (TabAdvance=aaNone) or
|
|
((TabAdvance=aaDown) and (Row>=GetLastVisibleRow)) or
|
|
(sh and (Col<=GetFirstVisibleColumn)) or
|
|
((not sh) and (Col>=GetLastVisibleColumn)) then
|
|
TabCheckEditorKey
|
|
else
|
|
Key := 0;
|
|
end else
|
|
TabCheckEditorKey;
|
|
VK_LEFT:
|
|
//Don't move to another cell is user is editing
|
|
if not FEditorKey then
|
|
begin
|
|
if Relaxed then
|
|
MoveSel(True, -cBidiMove[UseRightToLeftAlignment], 0)
|
|
else
|
|
MoveSel(True, 0,-1);
|
|
end;
|
|
VK_RIGHT:
|
|
//Don't move to another cell is user is editing
|
|
if not FEditorKey then
|
|
begin
|
|
if Relaxed then
|
|
MoveSel(True, cBidiMove[UseRightToLeftAlignment], 0)
|
|
else
|
|
MoveSel(True, 0, 1);
|
|
end;
|
|
VK_UP:
|
|
MoveSel(True, 0, -1);
|
|
VK_DOWN:
|
|
MoveSel(True, 0, 1);
|
|
VK_PRIOR:
|
|
begin
|
|
R:=FGCache.FullVisiblegrid;
|
|
MoveSel(True, 0, R.Top-R.Bottom);
|
|
end;
|
|
VK_NEXT:
|
|
begin
|
|
R:=FGCache.FullVisibleGrid;
|
|
MoveSel(True, 0, R.Bottom-R.Top);
|
|
end;
|
|
VK_HOME:
|
|
if not FEditorKey then begin
|
|
if ssCtrl in Shift then MoveSel(False, FCol, FFixedRows)
|
|
else
|
|
if Relaxed then MoveSel(False, FFixedCols, FRow)
|
|
else MoveSel(False, FCol, FFixedRows);
|
|
end;
|
|
VK_END:
|
|
if not FEditorKey then begin
|
|
if ssCtrl in Shift then MoveSel(False, FCol, RowCount-1)
|
|
else
|
|
if Relaxed then MoveSel(False, ColCount-1, FRow)
|
|
else MoveSel(False, FCol, RowCount-1);
|
|
end;
|
|
VK_APPS:
|
|
if not FEditorKey and EditingAllowed(FCol) then
|
|
EditorShow(False); // Will show popup menu in the editor.
|
|
VK_F2:
|
|
if not FEditorKey and EditingAllowed(FCol) then begin
|
|
SelectEditor;
|
|
EditorShow(False);
|
|
Key:=0;
|
|
end ;
|
|
VK_BACK:
|
|
// Workaround: LM_CHAR doesnt trigger with BACKSPACE
|
|
if not FEditorKey and EditingAllowed(FCol) then begin
|
|
EditorShowChar(^H);
|
|
key:=0;
|
|
end;
|
|
VK_C:
|
|
if not FEditorKey and (Shift = [ssModifier]) then
|
|
doCopyToClipboard;
|
|
VK_V:
|
|
if not FEditorKey and (Shift = [ssModifier]) then
|
|
doPasteFromClipboard;
|
|
VK_X:
|
|
if not FEditorKey and (Shift = [ssShift]) then
|
|
doCutToClipboard;
|
|
VK_DELETE:
|
|
if not FEditorKey and EditingAllowed(FCol) and
|
|
not (csDesigning in ComponentState) then begin
|
|
if Editor=nil then
|
|
SelectEditor;
|
|
if Editor is TCustomEdit then begin
|
|
EditorShow(False);
|
|
TCustomEdit(Editor).Text:='';
|
|
InvalidateCell(FCol,FRow,True);
|
|
EditorShow(True);
|
|
Key := 0;
|
|
end;
|
|
end;
|
|
VK_ESCAPE:
|
|
if (FEditor<>nil) and FEditor.Visible then
|
|
begin
|
|
EditordoResetValue;
|
|
EditorHide;
|
|
Key := 0;
|
|
end;
|
|
end;
|
|
if FEditorKey and (not PreserveRowAutoInserted) then
|
|
FRowAutoInserted:=False;
|
|
{$ifdef dbgGrid}DebugLn('Grid.KeyDown END Key=',IntToStr(Key));{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.KeyUp(var Key: Word; Shift: TShiftState);
|
|
begin
|
|
inherited KeyUp(Key, Shift);
|
|
end;
|
|
|
|
procedure TCustomGrid.KeyPress(var Key: char);
|
|
begin
|
|
inherited KeyPress(Key);
|
|
if not EditorKey then
|
|
// we are interested in these keys only if they came from the grid
|
|
if not EditorMode and EditingAllowed(FCol) then begin
|
|
if (Key=#13) then begin
|
|
SelectEditor;
|
|
EditorShow(True);
|
|
Key := #0;
|
|
end else
|
|
if (Key in [^H, #32..#255]) then begin
|
|
EditorShowChar(Key);
|
|
Key := #0;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ Convert a physical Mouse coordinate into a physical cell coordinate }
|
|
function TCustomGrid.MouseToCell(const Mouse: TPoint): TPoint;
|
|
begin
|
|
MouseToCell(Mouse.X, Mouse.Y, Result.X, Result.Y);
|
|
end;
|
|
|
|
procedure TCustomGrid.MouseToCell(X, Y: Integer; out ACol, ARow: Longint);
|
|
var
|
|
dummy: Integer;
|
|
begin
|
|
// Do not raise Exception if out of range
|
|
OffsetToColRow(True, True, X, ACol, dummy);
|
|
if ACol<0 then
|
|
ARow := -1
|
|
else begin
|
|
OffsetToColRow(False,True, Y, ARow, dummy);
|
|
if ARow<0 then
|
|
ACol := -1;
|
|
end;
|
|
end;
|
|
|
|
{ Convert a physical Mouse coordinate into a logical cell coordinate }
|
|
function TCustomGrid.MouseToLogcell(Mouse: TPoint): TPoint;
|
|
var
|
|
gz: TGridZone;
|
|
begin
|
|
Gz:=MouseToGridZone(Mouse.x, Mouse.y);
|
|
Result:=MouseToCell(Mouse);
|
|
if gz<>gzNormal then begin
|
|
if (gz=gzFixedRows)or(gz=gzFixedCells) then begin
|
|
Result.x:= fTopLeft.x-1;
|
|
if Result.x<FFixedCols then Result.x:=FFixedCols;
|
|
end;
|
|
if (gz=gzFixedCols)or(gz=gzFixedCells) then begin
|
|
Result.y:=fTopleft.y-1;
|
|
if Result.y<fFixedRows then Result.y:=FFixedRows;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.MouseCoord(X, Y: Integer): TGridCoord;
|
|
begin
|
|
Result := MouseToCell(Point(X,Y));
|
|
end;
|
|
|
|
function TCustomGrid.IsCellVisible(aCol, aRow: Integer): Boolean;
|
|
begin
|
|
with FGCache.VisibleGrid do
|
|
Result:= (Left<=ACol)and(aCol<=Right)and(Top<=aRow)and(aRow<=Bottom);
|
|
end;
|
|
|
|
function TCustomGrid.IsFixedCellVisible(aCol, aRow: Integer): boolean;
|
|
begin
|
|
with FGCache.VisibleGrid do
|
|
result := ((aCol<FixedCols) and ((aRow<FixedRows) or ((aRow>=Top)and(aRow<=Bottom)))) or
|
|
((aRow<FixedRows) and ((aCol<FixedCols) or ((aCol>=Left)and(aCol<=Right))));
|
|
end;
|
|
|
|
procedure TCustomGrid.InvalidateCol(ACol: Integer);
|
|
var
|
|
R: TRect;
|
|
begin
|
|
{$ifdef dbgPaint} DebugLn('InvalidateCol Col=',IntToStr(aCol)); {$Endif}
|
|
if not HandleAllocated then
|
|
exit;
|
|
R:=CellRect(aCol, FTopLeft.y);
|
|
R.Top:=0; // Full Column
|
|
R.Bottom:=FGCache.MaxClientXY.Y;
|
|
InvalidateRect(Handle, @R, True);
|
|
end;
|
|
|
|
procedure TCustomGrid.InvalidateFromCol(ACol: Integer);
|
|
var
|
|
R: TRect;
|
|
begin
|
|
{$IFDEF dbgPaint} DebugLn('InvalidateFromCol Col=',IntToStr(aCol)); {$Endif}
|
|
if not HandleAllocated then
|
|
exit;
|
|
R:=CellRect(aCol, FTopLeft.y);
|
|
R.Top:=0; // Full Column
|
|
R.BottomRight := FGCache.MaxClientXY;
|
|
InvalidateRect(Handle, @R, True);
|
|
end;
|
|
|
|
procedure TCustomGrid.InvalidateRow(ARow: Integer);
|
|
var
|
|
R: TRect;
|
|
begin
|
|
{$ifdef DbgPaint} DebugLn('InvalidateRow Row=',IntToStr(aRow)); {$Endif}
|
|
if not HandleAllocated then
|
|
exit;
|
|
R:=CellRect(fTopLeft.x, aRow);
|
|
if UseRightToLeftAlignment then begin
|
|
R.Left:=FlipX(FGCache.MaxClientXY.X);
|
|
R.Right:=FGCache.ClientRect.Right;
|
|
end
|
|
else begin
|
|
R.Left:=0; // Full row
|
|
R.Right:=FGCache.MaxClientXY.X;
|
|
end;
|
|
InvalidateRect(Handle, @R, True);
|
|
end;
|
|
|
|
procedure TCustomGrid.InvalidateFocused;
|
|
begin
|
|
if FGCache.ValidGrid then begin
|
|
{$ifdef dbgGrid}DebugLn('InvalidateFocused');{$Endif}
|
|
if ((goRowSelect in Options) or (goRowHighlight in Options)) then
|
|
InvalidateRow(Row)
|
|
else
|
|
InvalidateCell(Col,Row);
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.MoveExtend(Relative: Boolean; DCol, DRow: Integer;
|
|
ForceFullyVisible: Boolean): Boolean;
|
|
var
|
|
OldRange: TRect;
|
|
prevCol, prevRow: Integer;
|
|
begin
|
|
Result:=TryMoveSelection(Relative,DCol,DRow);
|
|
if (not Result) then Exit;
|
|
|
|
Result:=EditorGetValue(true);
|
|
if (not Result) then Exit;
|
|
|
|
{$IfDef dbgGrid}DebugLnEnter('MoveExtend INIT FCol= ',IntToStr(FCol), ' FRow= ',IntToStr(FRow));{$Endif}
|
|
BeforeMoveSelection(DCol,DRow);
|
|
|
|
OldRange := FRange;
|
|
PrevRow := FRow;
|
|
PrevCol := FCol;
|
|
|
|
if goRowSelect in Options then
|
|
FRange:=Rect(FFixedCols, DRow, Colcount-1, DRow)
|
|
else
|
|
FRange:=Rect(DCol,DRow,DCol,DRow);
|
|
|
|
if SelectActive and (goRangeSelect in Options) then
|
|
if goRowSelect in Options then begin
|
|
FRange.Top:=Min(fPivot.y, DRow);
|
|
FRange.Bottom:=Max(fPivot.y, DRow);
|
|
end else
|
|
FRange:=NormalizarRect(Rect(Fpivot.x,FPivot.y, DCol, DRow));
|
|
|
|
if not ScrollToCell(DCol, DRow, ForceFullyVisible) then
|
|
InvalidateMovement(DCol, DRow, OldRange);
|
|
|
|
FCol := DCol;
|
|
FRow := DRow;
|
|
|
|
MoveSelection;
|
|
SelectEditor;
|
|
|
|
if (FEditor<>nil) and EditorAlwaysShown then begin
|
|
// if editor visibility was changed on BeforeMoveSelection or MoveSelection
|
|
// make sure editor will be updated.
|
|
// TODO: cell coords of last time editor was visible
|
|
// could help here too, if they are not the same as the
|
|
// current cell, editor should be hidden first too.
|
|
if FEditor.Visible then
|
|
EditorHide;
|
|
EditorShow(true);
|
|
end;
|
|
|
|
AfterMoveSelection(PrevCol,PrevRow);
|
|
|
|
{$IfDef dbgGrid}DebugLnExit('MoveExtend END FCol= ',IntToStr(FCol), ' FRow= ',IntToStr(FRow));{$Endif}
|
|
end;
|
|
|
|
function TCustomGrid.MoveNextAuto(const Inverse: boolean): boolean;
|
|
var
|
|
aCol,aRow: Integer;
|
|
begin
|
|
Result := GetDeltaMoveNext(Inverse, ACol, ARow, FAutoAdvance);
|
|
if Result then
|
|
MoveNextSelectable(true, aCol, aRow);
|
|
end;
|
|
|
|
function TCustomGrid.MoveNextSelectable(Relative: Boolean; DCol, DRow: Integer): Boolean;
|
|
var
|
|
CInc,RInc: Integer;
|
|
NCol,NRow: Integer;
|
|
begin
|
|
// Reference
|
|
if not Relative then begin
|
|
NCol:=DCol;
|
|
NRow:=DRow;
|
|
DCol:=NCol-FCol;
|
|
DRow:=NRow-FRow;
|
|
end else begin
|
|
NCol:=FCol+DCol;
|
|
NRow:=FRow+DRow;
|
|
if (goEditing in options) and (goAutoAddRows in options) then begin
|
|
if (DRow=1) and (NRow>=RowCount) then begin
|
|
// If the last row has data or goAutoAddRowsSkipContentCheck is set, add a new row.
|
|
if (not FRowAutoInserted) then begin
|
|
if (goAutoAddRowsSkipContentCheck in Options) or (not IsEmptyRow(FRow)) then begin
|
|
RowCount:=RowCount+1;
|
|
if not (goAutoAddRowsSkipContentCheck in Options) then FRowAutoInserted:=True;
|
|
end;
|
|
end;
|
|
end
|
|
else if FRowAutoInserted and (DRow=-1) then begin
|
|
RowCount:=RowCount-1;
|
|
FRowAutoInserted:=False;
|
|
ScrollToCell(Col, Row, True);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Checklimits(NCol, NRow);
|
|
|
|
// Increment
|
|
if DCol<0 then CInc:=-1 else
|
|
if DCol>0 then CInc:= 1
|
|
else CInc:= 0;
|
|
if DRow<0 then RInc:=-1 else
|
|
if DRow>0 then RInc:= 1
|
|
else RInc:= 0;
|
|
|
|
// Calculate
|
|
Result:=False;
|
|
while ((ColWidths[NCol]=0) and (CInc<>0))
|
|
or ((RowHeights[NRow]=0) and (RInc<>0)) do
|
|
begin
|
|
if not (IsRowIndexVariable(NRow+RInc) and IsColumnIndexVariable(NCol+CInc)) then
|
|
Exit;
|
|
Inc(NCol, CInc);
|
|
Inc(NRow, RInc);
|
|
end;
|
|
Result:=MoveExtend(False, NCol, NRow, True);
|
|
|
|
// whether or not a movement was valid if goAlwaysShowEditor
|
|
// is set, editor should pop up.
|
|
if not EditorMode and EditorAlwaysShown then begin
|
|
SelectEditor;
|
|
if Feditor<>nil then
|
|
EditorShow(true);
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.TryMoveSelection(Relative: Boolean; var DCol, DRow: Integer
|
|
): Boolean;
|
|
begin
|
|
Result:=False;
|
|
|
|
if FixedGrid then
|
|
exit;
|
|
|
|
if Relative then begin
|
|
Inc(DCol, FCol);
|
|
Inc(DRow, FRow);
|
|
end;
|
|
|
|
CheckLimits(DCol, DRow);
|
|
|
|
// Change on Focused cell?
|
|
if (DCol=FCol) and (DRow=FRow) then
|
|
SelectCell(DCol,DRow)
|
|
else
|
|
Result:=SelectCell(DCol,DRow);
|
|
end;
|
|
|
|
procedure TCustomGrid.UnLockEditor;
|
|
begin
|
|
if FEDitorHidingCount>0 then
|
|
Dec(FEditorHidingCount)
|
|
else
|
|
DebugLn('WARNING: unpaired Unlock Editor');
|
|
{$ifdef dbgGrid}DebugLn('==< LockEditor: ', dbgs(FEditorHidingCount)); {$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.UpdateHorzScrollBar(const aVisible: boolean;
|
|
const aRange,aPage,aPos: Integer);
|
|
var
|
|
NeedUpdate: Boolean;
|
|
begin
|
|
{$ifdef DbgScroll}
|
|
DebugLn('TCustomGrid.UpdateHorzScrollbar: Vis=%s Range=%d Page=%d aPos=%d',
|
|
[dbgs(aVisible),aRange, aPage, aPos]);
|
|
{$endif}
|
|
NeedUpdate := FHSbVisible <> Ord(AVisible);
|
|
if NeedUpdate then
|
|
ScrollBarShow(SB_HORZ, aVisible);
|
|
if aVisible or NeedUpdate then
|
|
ScrollBarRange(SB_HORZ, aRange, aPage, aPos);
|
|
end;
|
|
|
|
procedure TCustomGrid.UpdateVertScrollbar(const aVisible: boolean;
|
|
const aRange,aPage,aPos: Integer);
|
|
begin
|
|
{$ifdef DbgScroll}
|
|
DebugLn('TCustomGrid.UpdateVertScrollbar: Vis=%s Range=%d Page=%d aPos=%d',
|
|
[dbgs(aVisible),aRange, aPage, aPos]);
|
|
{$endif}
|
|
if FVSbVisible<>Ord(aVisible) then
|
|
ScrollBarShow(SB_VERT, aVisible);
|
|
if aVisible then
|
|
ScrollbarRange(SB_VERT, aRange, aPage, aPos );
|
|
end;
|
|
|
|
procedure TCustomGrid.UpdateBorderStyle;
|
|
var
|
|
ABorderStyle: TBorderStyle;
|
|
begin
|
|
if not Flat and (FGridBorderStyle=bsSingle) then
|
|
ABorderStyle := bsSingle
|
|
else
|
|
ABorderStyle := bsNone;
|
|
inherited SetBorderStyle(ABorderStyle);
|
|
if HandleAllocated and ([csDestroying,csLoading]*ComponentState=[]) then
|
|
begin
|
|
VisualChange;
|
|
if CheckTopLeft(Col, Row, True, True) then
|
|
VisualChange;
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.ValidateEntry(const ACol, ARow: Integer;
|
|
const OldValue:string; var NewValue:string): boolean;
|
|
begin
|
|
result := true;
|
|
if assigned(OnValidateEntry) then begin
|
|
try
|
|
OnValidateEntry(Self, ACol, ARow, OldValue, NewValue);
|
|
except
|
|
on E:Exception do begin
|
|
result := false;
|
|
if FGridState=gsSelecting then
|
|
FGridState := gsNormal;
|
|
Application.HandleException(E);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.BeforeMoveSelection(const DCol,DRow: Integer);
|
|
begin
|
|
if Assigned(OnBeforeSelection) then OnBeforeSelection(Self, DCol, DRow);
|
|
end;
|
|
|
|
procedure TCustomGrid.BeginAutoDrag;
|
|
begin
|
|
if ((goColSizing in Options) and (FCursorState=gcsColWidthChanging)) or
|
|
((goRowSizing in Options) and (FCursorState=gcsRowHeightChanging))
|
|
then
|
|
// TODO: Resizing in progress, add an option to forbid resizing
|
|
// when DragMode=dmAutomatic
|
|
else
|
|
BeginDrag(False);
|
|
end;
|
|
|
|
procedure TCustomGrid.CalcAutoSizeColumn(const Index: Integer; var AMin, AMax,
|
|
APriority: Integer);
|
|
begin
|
|
APriority := 0;
|
|
end;
|
|
|
|
procedure TCustomGrid.CalcCellExtent(acol, aRow: Integer; var aRect: TRect);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TCustomGrid.CalcFocusRect(var ARect: TRect; adjust: boolean = true);
|
|
begin
|
|
if goRowSelect in Options then begin
|
|
|
|
if UseRightToLeftAlignment then begin
|
|
aRect.Left := GCache.ClientWidth - GCache.MaxClientXY.x;
|
|
aRect.Right := GCache.ClientWidth - GCache.FixedWidth;
|
|
end else begin
|
|
aRect.Left := GCache.FixedWidth;
|
|
aRect.Right := GCache.MaxClientXY.x;
|
|
end;
|
|
|
|
FlipRect(aRect);
|
|
end;
|
|
|
|
if not adjust then
|
|
exit;
|
|
|
|
if goHorzLine in Options then
|
|
dec(aRect.Bottom, 1 + FGridLineWidth div 2);
|
|
|
|
if goVertLine in Options then
|
|
if UseRightToLeftAlignment then
|
|
inc(aRect.Left, 1 + FGridLineWidth div 2)
|
|
else
|
|
dec(aRect.Right, 1 + FGridLineWidth div 2);
|
|
end;
|
|
|
|
procedure TCustomGrid.CalcScrollbarsRange;
|
|
var
|
|
HsbVisible, VsbVisible: boolean;
|
|
HsbRange,VsbRange: Integer;
|
|
HsbPage, VsbPage: Integer;
|
|
HsbPos, VsbPos: Integer;
|
|
begin
|
|
with FGCache do begin
|
|
GetSBVisibility(HsbVisible, VsbVisible);
|
|
GetSBRanges(HsbVisible,VsbVisible,HsbRange,VsbRange,HsbPage,VsbPage,HsbPos,VsbPos);
|
|
UpdateVertScrollBar(VsbVisible, VsbRange, VsbPage, VsbPos);
|
|
UpdateHorzScrollBar(HsbVisible, HsbRange, HsbPage, HsbPos);
|
|
{$ifdef DbgScroll}
|
|
DebugLn('VRange=',dbgs(VsbRange),' Visible=',dbgs(VSbVisible));
|
|
DebugLn('HRange=',dbgs(HsbRange),' Visible=',dbgs(HSbVisible));
|
|
{$endif}
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.CalculatePreferredSize(var PreferredWidth,
|
|
PreferredHeight: integer; WithThemeSpace: Boolean);
|
|
begin
|
|
PreferredWidth:=0;
|
|
PreferredHeight:=0;
|
|
end;
|
|
|
|
procedure TCustomGrid.CalcMaxTopLeft;
|
|
var
|
|
i: Integer;
|
|
W,H: Integer;
|
|
begin
|
|
FGCache.MaxTopLeft:=Point(ColCount-1, RowCount-1);
|
|
FGCache.MaxTLOffset.x:=0;
|
|
FGCache.MaxTLOffset.y:=0;
|
|
W:=0;
|
|
if not(goScrollToLastCol in FOptions2) then
|
|
begin
|
|
for i:=ColCount-1 downto FFixedCols do
|
|
begin
|
|
W:=W+GetColWidths(i);
|
|
if W<=FGCache.ScrollWidth then
|
|
FGCache.MaxTopLeft.x:=i
|
|
else
|
|
begin
|
|
if GetSmoothScroll(SB_Horz) then
|
|
begin
|
|
FGCache.MaxTopLeft.x:=i;
|
|
FGCache.MaxTLOffset.x:=W-FGCache.ScrollWidth;
|
|
end;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
H:=0;
|
|
if not(goScrollToLastRow in FOptions2) then
|
|
begin
|
|
for i:=RowCount-1 downto FFixedRows do
|
|
begin
|
|
H:=H+GetRowHeights(i);
|
|
if H<=FGCache.ScrollHeight then
|
|
FGCache.MaxTopLeft.y:=i
|
|
else
|
|
begin
|
|
if GetSmoothScroll(SB_Vert) then
|
|
begin
|
|
FGCache.MaxTopLeft.y:=i;
|
|
FGCache.MaxTLOffset.y:=H-FGCache.ScrollHeight
|
|
end;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
FGCache.MaxTopLeft.x:=Max(FGCache.MaxTopLeft.x, FixedCols);
|
|
FGCache.MaxTopLeft.y:=Max(FGCache.MaxTopLeft.y, FixedRows);
|
|
end;
|
|
|
|
procedure TCustomGrid.CellClick(const aCol, aRow: Integer; const Button:TMouseButton);
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomGrid.CellExtent(const aCol, aRow: Integer; var R: TRect; out
|
|
exCol: Integer);
|
|
var
|
|
Extent: TRect;
|
|
begin
|
|
Extent := R;
|
|
exCol := aCol;
|
|
CalcCellExtent(aCol, aRow, R);
|
|
// TODO: check RTL
|
|
while (exCol<=FGCache.VisibleGrid.Right) and (Extent.Right<R.Right) do begin
|
|
inc(exCol);
|
|
ColRowToOffset(True, True, exCol, Extent.Left, Extent.Right);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.CheckLimits(var aCol, aRow: Integer);
|
|
begin
|
|
if aCol<FFixedCols then aCol:=FFixedCols else
|
|
if aCol>ColCount-1 then acol:=ColCount-1;
|
|
if aRow<FFixedRows then aRow:=FFixedRows else
|
|
if aRow>RowCount-1 then aRow:=RowCount-1;
|
|
end;
|
|
|
|
// We don't want to do this inside CheckLimits() because keyboard handling
|
|
// shouldn't raise an error whereas setting the Row or Col property it should.
|
|
procedure TCustomGrid.CheckLimitsWithError(const aCol, aRow: Integer);
|
|
begin
|
|
if not IsColumnIndexValid(aCol) or not IsRowIndexValid(aRow) then
|
|
raise EGridException.Create(rsGridIndexOutOfRange);
|
|
end;
|
|
|
|
procedure TCustomGrid.ClearSelections;
|
|
begin
|
|
SetLength(FSelections, 0);
|
|
UpdateSelectionRange;
|
|
FPivot := Point(Col, Row);
|
|
InvalidateGrid;
|
|
end;
|
|
|
|
procedure TCustomGrid.CMBiDiModeChanged(var Message: TLMessage);
|
|
begin
|
|
VisualChange;
|
|
inherited CMBidiModeChanged(Message);
|
|
end;
|
|
|
|
procedure TCustomGrid.CMMouseEnter(var Message: TLMessage);
|
|
begin
|
|
inherited;
|
|
FSavedHint := Hint;
|
|
// Note: disable hint when entering grid's border, we'll manage our own hints
|
|
Application.Hint := '';
|
|
Application.CancelHint;
|
|
end;
|
|
|
|
procedure TCustomGrid.CMMouseLeave(var Message: TLMessage);
|
|
begin
|
|
Hint := FSavedHint;
|
|
ResetHotCell;
|
|
inherited CMMouseLeave(Message);
|
|
end;
|
|
|
|
// This procedure checks if cursor cell position is allowed
|
|
// if not it tries to find a suitable position based on
|
|
// AutoAdvance and SelectCell.
|
|
procedure TCustomGrid.CheckPosition;
|
|
var
|
|
OldAA: TAutoAdvance;
|
|
DeltaCol,DeltaRow: Integer;
|
|
begin
|
|
// first tries to find if current position is allowed
|
|
if SelectCell(Col,Row) then
|
|
exit;
|
|
|
|
// current position is not valid, look for another position
|
|
OldAA := FAutoAdvance;
|
|
|
|
if OldAA=aaNone then
|
|
FAutoAdvance := aaRightDown;
|
|
|
|
try
|
|
// try first normal movement then inverse movement
|
|
if GetDeltaMoveNext(false, DeltaCol,DeltaRow,FAutoAdvance) or
|
|
GetDeltaMoveNext(true, DeltaCol,DeltaRow,FAutoAdvance)
|
|
then begin
|
|
MoveNextSelectable(True, DeltaCol, DeltaRow)
|
|
end else begin
|
|
// some combinations of AutoAdvance and current position
|
|
// will always fail, for example if user set current
|
|
// column not selectable and autoadvance is aaDown will
|
|
// fail always, in this case as a last resource do a full
|
|
// scan until a cell is available
|
|
for DeltaCol:=FixedCols to ColCount-1 do
|
|
for DeltaRow:=FixedRows to RowCount-1 do begin
|
|
if SelectCell(DeltaCol,DeltaRow) then begin
|
|
// found one selectable cell
|
|
MoveNextSelectable(False, DeltaCol,DeltaRow);
|
|
exit;
|
|
end;
|
|
end;
|
|
// user has created weird situation.
|
|
// can't do more about it.
|
|
end;
|
|
|
|
finally
|
|
FAutoAdvance := OldAA;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.MoveSelection;
|
|
begin
|
|
if Assigned(OnSelection) then OnSelection(Self, FCol, FRow);
|
|
end;
|
|
|
|
procedure TCustomGrid.Notification(AComponent: TComponent; Operation: TOperation);
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if (Operation = opRemove) and (AComponent = FTitleImageList) then
|
|
begin
|
|
FTitleImageList := nil;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.BeginUpdate;
|
|
begin
|
|
Inc(FUpdateCount);
|
|
end;
|
|
|
|
function TCustomGrid.BoxRect(ALeft, ATop, ARight, ABottom: Longint): TRect;
|
|
begin
|
|
if ARight<ALeft then
|
|
SwapInt(ALeft, ARight);
|
|
if ABottom<ATop then
|
|
SwapInt(ATop, ABottom);
|
|
|
|
Result := CellRect(ALeft, ATop);
|
|
Result.BottomRight := CellRect(ARight, ABottom).BottomRight;
|
|
|
|
IntersectRect(Result, Result, FGCache.VisibleGrid);
|
|
end;
|
|
|
|
procedure TCustomGrid.CacheMouseDown(const X, Y: Integer);
|
|
var
|
|
ParentForm: TCustomForm;
|
|
begin
|
|
FGCache.ClickMouse := Point(X,Y);
|
|
FGCache.ClickCell := MouseToCell(FGCache.ClickMouse);
|
|
if (FGCache.HotGridZone=gzInvalid) then begin
|
|
ParentForm := GetParentForm(Self);
|
|
if (ParentForm<>nil) and ParentForm.Active then
|
|
FGCache.HotGridZone := CellToGridZone(FGCache.ClickCell.X, FGCache.ClickCell.Y);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EndUpdate(aRefresh: boolean = true);
|
|
begin
|
|
Dec(FUpdateCount);
|
|
if (FUpdateCount=0) and aRefresh then
|
|
VisualChange;
|
|
end;
|
|
|
|
procedure TCustomGrid.EraseBackground(DC: HDC);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
function TCustomGrid.Focused: Boolean;
|
|
begin
|
|
Result := CanTab and (HandleAllocated and
|
|
(FindOwnerControl(GetFocus)=Self) or
|
|
((FEditor<>nil) and FEditor.Visible and FEditor.Focused));
|
|
end;
|
|
|
|
procedure TCustomGrid.InvalidateCell(aCol, aRow: Integer);
|
|
begin
|
|
InvalidateCell(ACol,ARow, False);
|
|
end;
|
|
|
|
function TCustomGrid.HasMultiSelection: Boolean;
|
|
begin
|
|
Result := (goRangeSelect in Options) and
|
|
(FRangeSelectMode = rsmMulti) and (Length(FSelections) > 0);
|
|
end;
|
|
|
|
procedure TCustomGrid.InvalidateCell(aCol, aRow: Integer; Redraw: Boolean);
|
|
var
|
|
R: TRect;
|
|
begin
|
|
{$IfDef dbgPaint}
|
|
DebugLn(['InvalidateCell Col=',aCol,
|
|
' Row=',aRow,' Redraw=', Redraw]);
|
|
{$Endif}
|
|
if HandleAllocated and (IsCellVisible(aCol, aRow) or IsFixedCellVisible(aCol, aRow)) then begin
|
|
R:=CellRect(aCol, aRow);
|
|
InvalidateRect(Handle, @R, Redraw);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.InvalidateRange(const aRange: TRect);
|
|
var
|
|
RIni,RFin: TRect;
|
|
begin
|
|
if not HandleAllocated then
|
|
exit;
|
|
RIni := CellRect(aRange.Left, aRange.Top);
|
|
RFin := CellRect(aRange.Right, aRange.Bottom);
|
|
if UseRightToLeftAlignment then
|
|
RIni.Left := RFin.Left
|
|
else
|
|
RIni.Right := RFin.Right;
|
|
RIni.Bottom:= RFin.Bottom;
|
|
InvalidateRect(Handle, @RIni, False);
|
|
end;
|
|
|
|
procedure TCustomGrid.InvalidateGrid;
|
|
begin
|
|
if FUpdateCount=0 then Invalidate;
|
|
end;
|
|
|
|
procedure TCustomGrid.Invalidate;
|
|
begin
|
|
if FUpdateCount=0 then begin
|
|
{$IfDef dbgPaint} DebugLn('Invalidate');{$Endif}
|
|
inherited Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditingDone;
|
|
begin
|
|
if not FEditorShowing then
|
|
inherited EditingDone;
|
|
end;
|
|
|
|
function TCustomGrid.EditorGetValue(validate:boolean=false): boolean;
|
|
var
|
|
CurValue,NewValue: string;
|
|
begin
|
|
result := true;
|
|
if (([csDesigning, csDestroying] * ComponentState) = [])
|
|
and (Editor<>nil) and Editor.Visible then begin
|
|
|
|
if validate then begin
|
|
CurValue := GetCells(FCol,FRow);
|
|
NewValue := CurValue;
|
|
result := ValidateEntry(FCol,FRow,FEditorOldValue,NewValue);
|
|
if (CurValue<>NewValue) then begin
|
|
SetEditText(FCol,FRow,NewValue);
|
|
if result then
|
|
EditorHide
|
|
else
|
|
EditorDoSetValue;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
if result then begin
|
|
EditorDoGetValue;
|
|
EditorHide;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorSetValue;
|
|
begin
|
|
if not (csDesigning in ComponentState) then begin
|
|
EditorPos;
|
|
EditordoSetValue;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorHide;
|
|
var
|
|
WasFocused: boolean;
|
|
begin
|
|
if not EditorLocked and (Editor<>nil) and Editor.Visible then
|
|
begin
|
|
FEditorMode := False;
|
|
FGridState := gsNormal;
|
|
if Editor.Parent<>nil then // May be nil when the form is closing.
|
|
begin
|
|
WasFocused := Editor.Focused;
|
|
{$ifdef dbgGrid}DebugLnEnter('EditorHide [',Editor.ClassName,'] INIT FCol=',IntToStr(FCol),' FRow=',IntToStr(FRow));{$endif}
|
|
LockEditor;
|
|
try
|
|
DoEditorHide;
|
|
finally
|
|
if WasFocused then
|
|
SetFocus;
|
|
UnLockEditor;
|
|
end;
|
|
{$ifdef dbgGrid}DebugLnExit('EditorHide END');{$endif}
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.EditorLocked: boolean;
|
|
begin
|
|
Result := FEditorHidingCount <> 0;
|
|
end;
|
|
|
|
function TCustomGrid.EditingAllowed(ACol: Integer = -1): Boolean;
|
|
var
|
|
C: TGridColumn;
|
|
begin
|
|
Result:=(goEditing in options) and IsColumnIndexValid(ACol) and (RowCount>FixedRows);
|
|
if Result and Columns.Enabled then begin
|
|
C:=ColumnFromGridColumn(ACol);
|
|
Result:=(C<>nil) and (not C.ReadOnly);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorShow(const SelAll: boolean);
|
|
begin
|
|
if ([csLoading,csDestroying,csDesigning]*ComponentState<>[])
|
|
or (not Enabled) or (not IsVisible)
|
|
or (not HandleAllocated) then
|
|
Exit;
|
|
|
|
if EditingAllowed(FCol) and CanEditShow and (not FEditorShowing) and
|
|
(Editor<>nil) and (not Editor.Visible) and (not EditorLocked) then
|
|
begin
|
|
{$ifdef dbgGrid} DebugLnEnter('EditorShow [',Editor.ClassName,'] INIT FCol=',IntToStr(FCol),' FRow=',IntToStr(FRow));{$endif}
|
|
FEditorMode:=True;
|
|
FEditorOldValue := GetCells(FCol,FRow);
|
|
FEditorShowing:=True;
|
|
doEditorShow;
|
|
FEditorShowing:=False;
|
|
if SelAll then
|
|
EditorSelectAll;
|
|
FGridState := gsNormal;
|
|
{$ifdef dbgGrid} DebugLnExit('EditorShow END');{$endif}
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorShowInCell(const aCol, aRow: Integer);
|
|
var
|
|
OldCol,OldRow: Integer;
|
|
begin
|
|
OldCol:=FCol;
|
|
OldRow:=FRow;
|
|
try
|
|
EditorGetValue;
|
|
FCol:=aCol;
|
|
FRow:=aRow;
|
|
SelectEditor;
|
|
EditorShow(True);
|
|
finally
|
|
if (FCol=aCol)and(FRow=aRow) then
|
|
begin
|
|
// Current col,row didn't change, restore old ones
|
|
FCol:=OldCol;
|
|
FRow:=OldRow;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorTextChanged(const aCol,aRow: Integer; const aText:string);
|
|
begin
|
|
SetEditText(aCol, aRow, aText);
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorWidthChanged(aCol, aWidth: Integer);
|
|
begin
|
|
EditorPos;
|
|
end;
|
|
|
|
function TCustomGrid.FirstGridColumn: integer;
|
|
begin
|
|
result := FixedCols;
|
|
end;
|
|
|
|
procedure TCustomGrid.FixDesignFontsPPI(const ADesignTimePPI: Integer);
|
|
var
|
|
LTitleFontIsDefault: Boolean;
|
|
I: Integer;
|
|
begin
|
|
inherited FixDesignFontsPPI(ADesignTimePPI);
|
|
|
|
LTitleFontIsDefault := FTitleFontIsDefault;
|
|
DoFixDesignFontPPI(TitleFont, ADesignTimePPI);
|
|
FTitleFontIsDefault := LTitleFontIsDefault;
|
|
for I := 0 to FColumns.Count-1 do
|
|
FColumns[I].FixDesignFontsPPI(ADesignTimePPI);
|
|
end;
|
|
|
|
function TCustomGrid.FixedGrid: boolean;
|
|
begin
|
|
result := (FixedCols=ColCount) or (FixedRows=RowCount)
|
|
end;
|
|
|
|
procedure TCustomGrid.FontChanged(Sender: TObject);
|
|
begin
|
|
FRealizedDefRowHeight := 0;
|
|
FRealizedDefColWidth := 0;
|
|
if csCustomPaint in ControlState then
|
|
Canvas.Font := Font
|
|
else begin
|
|
inherited FontChanged(Sender);
|
|
if FColumns.Enabled then
|
|
FColumns.FontChanged;
|
|
if FTitleFontIsDefault then begin
|
|
FTitleFont.Assign(Font);
|
|
FTitleFontIsDefault := True;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorPos;
|
|
var
|
|
msg: TGridMessage;
|
|
CellR, editorBounds: TRect;
|
|
PosValid: Boolean;
|
|
|
|
procedure CalcEditorBounds(aEditor: TWinControl; var refRect: TRect);
|
|
begin
|
|
if (aEditor = FStringEditor) and (EditorBorderStyle = bsNone) then
|
|
refRect := TWSCustomGridClass(WidgetSetClass).
|
|
GetEditorBoundsFromCellRect(Canvas, refRect, GetColumnLayout(FCol, False))
|
|
else
|
|
AdjustInnerCellRect(refRect);
|
|
end;
|
|
|
|
begin
|
|
{$ifdef dbgGrid} DebugLn('Grid.EditorPos INIT');{$endif}
|
|
if HandleAllocated and (FEditor<>nil) then begin
|
|
|
|
// send editor position
|
|
Msg.LclMsg.msg:=GM_SETPOS;
|
|
Msg.Grid:=Self;
|
|
Msg.Col:=FCol;
|
|
Msg.Row:=FRow;
|
|
FEditor.Dispatch(Msg);
|
|
|
|
// send editor bounds
|
|
PosValid := ColRowToOffset(True, True, FCol, CellR.Left, CellR.Right)
|
|
and ColRowToOffSet(False,True, FRow, CellR.Top, CellR.Bottom);
|
|
if not PosValid then // Can't position editor; ensure sane values
|
|
CellR := Rect(0,0,FEditor.Width, FEditor.Height);
|
|
|
|
if not PosValid or (CellR.Top<FGCache.FixedHeight) or (CellR.Top>FGCache.ClientHeight) or
|
|
(UseRightToLeftAlignment and ((CellR.Right-1>FlipX(FGCache.FixedWidth)) or (CellR.Right<0))) or
|
|
(not UseRightToLeftAlignment and ((CellR.Left<FGCache.FixedWidth) or (CellR.Left>FGCache.ClientWidth)))
|
|
then
|
|
// if editor will be out of sight, make the out of sight coords fixed
|
|
// this should avoid range check errors on widgetsets that can't handle
|
|
// high control coords (like GTK2)
|
|
CellR := Bounds(-FEditor.Width-100, -FEditor.Height-100, CellR.Right-CellR.Left, CellR.Bottom-CellR.Top);
|
|
|
|
// Make sure to use the grid font, not that of the title (issue #38203).
|
|
Canvas.Font.Assign(Font);
|
|
|
|
if FEditorOptions and EO_AUTOSIZE = EO_AUTOSIZE then begin
|
|
CalcEditorBounds(FEditor, CellR);
|
|
FEditor.BoundsRect := CellR;
|
|
end else begin
|
|
if FEditor=FButtonStringEditor then begin
|
|
// here we ensure that FStringEditor which is the ActiveControl in
|
|
// FButtonStringEditor get its bounds right
|
|
editorBounds := CellR;
|
|
CalcEditorBounds(FStringEditor, editorBounds);
|
|
FStringEditor.BoundsRect := editorBounds;
|
|
end;
|
|
Msg.LclMsg.msg:=GM_SETBOUNDS;
|
|
Msg.CellRect:=CellR;
|
|
Msg.Grid:=Self;
|
|
Msg.Col:=FCol;
|
|
Msg.Row:=FRow;
|
|
FEditor.Dispatch(Msg);
|
|
end;
|
|
end;
|
|
{$ifdef dbgGrid} DebugLn('Grid.EditorPos END');{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorSelectAll;
|
|
var
|
|
Msg: TGridMessage;
|
|
begin
|
|
{$ifdef dbgGrid}DebugLn('EditorSelectALL INIT');{$endif}
|
|
if FEditor<>nil then
|
|
if FEditorOptions and EO_SELECTALL = EO_SELECTALL then begin
|
|
Msg.LclMsg.msg:=GM_SELECTALL;
|
|
FEditor.Dispatch(Msg);
|
|
end;
|
|
{$ifdef dbgGrid}DebugLn('EditorSelectALL END');{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.EditordoGetValue;
|
|
var
|
|
msg: TGridMessage;
|
|
begin
|
|
if (FEditor<>nil) and FEditor.Visible then begin
|
|
Msg.LclMsg.msg:=GM_GETVALUE;
|
|
Msg.grid:=Self;
|
|
Msg.Col:=FCol;
|
|
Msg.Row:=FRow;
|
|
Msg.Value:=GetCells(FCol, FRow);
|
|
FEditor.Dispatch(Msg);
|
|
SetEditText(Msg.Col, Msg.Row, Msg.Value);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditordoResetValue;
|
|
var
|
|
msg: TGridMessage;
|
|
begin
|
|
if (FEditor<>nil) and FEditor.Visible then begin
|
|
Msg.LclMsg.msg:=GM_SETVALUE;
|
|
Msg.grid:=Self;
|
|
Msg.Col:=FCol;
|
|
Msg.Row:=FRow;
|
|
Msg.Value:=FEditorOldValue;
|
|
FEditor.Dispatch(Msg);
|
|
SetEditText(Msg.Col, Msg.Row, Msg.Value);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditordoSetValue;
|
|
var
|
|
msg: TGridMessage;
|
|
begin
|
|
if FEditor<>nil then begin
|
|
// Set the editor mask
|
|
Msg.LclMsg.msg:=GM_SETMASK;
|
|
Msg.Grid:=Self;
|
|
Msg.Col:=FCol;
|
|
Msg.Row:=FRow;
|
|
Msg.Value:=GetEditMask(FCol, FRow);
|
|
FEditor.Dispatch(Msg);
|
|
// Set the editor value
|
|
Msg.LclMsg.msg:=GM_SETVALUE;
|
|
Msg.Grid:=Self;
|
|
Msg.Col:=FCol;
|
|
Msg.Row:=FRow;
|
|
Msg.Value:=GetEditText(Fcol, FRow);
|
|
FEditor.Dispatch(Msg);
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.EditorCanAcceptKey(const ch: TUTF8Char): boolean;
|
|
begin
|
|
result := True;
|
|
end;
|
|
|
|
function TCustomGrid.EditorIsReadOnly: boolean;
|
|
begin
|
|
result := GetColumnReadonly(Col);
|
|
end;
|
|
|
|
procedure TCustomGrid.GetAutoFillColumnInfo(const Index: Integer; var aMin,aMax,aPriority: Integer);
|
|
var
|
|
C: TGridColumn;
|
|
begin
|
|
aMin := DEFMINSIZE;
|
|
aMax := DEFMAXSIZE;
|
|
if Index<FixedCols then
|
|
APriority := 0
|
|
else if Columns.Enabled then begin
|
|
C := ColumnFromGridColumn(Index);
|
|
if C<>nil then begin
|
|
aMin := C.MinSize;
|
|
aMax := C.MaxSize;
|
|
aPriority := C.SizePriority;
|
|
end else
|
|
APriority := 1;
|
|
end else
|
|
APriority := 1;
|
|
end;
|
|
|
|
function TCustomGrid.GetCellHintText(ACol, ARow: Integer): string;
|
|
begin
|
|
Result := '';
|
|
if Assigned(FOnGetCellHint) then
|
|
FOnGetCellHint(self, ACol, ARow, result);
|
|
end;
|
|
|
|
function TCustomGrid.GetTruncCellHintText(ACol, ARow: Integer): string;
|
|
begin
|
|
Result := GetCells(ACol, ARow);
|
|
if Assigned(FOnGetCellHint) and (FCellHintPriority = chpTruncOnly) then
|
|
FOnGetCellHint(self, ACol, ARow, result);
|
|
end;
|
|
|
|
function TCustomGrid.GetCells(ACol, ARow: Integer): string;
|
|
begin
|
|
result := '';
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorKeyDown(Sender: TObject; var Key:Word; Shift:TShiftState);
|
|
begin
|
|
{$ifdef dbgGrid}DebugLn('Grid.EditorKeyDown Key=',dbgs(Key),' INIT');{$endif}
|
|
FEditorKey:=True; // Just a flag to see from where the event comes
|
|
KeyDown(Key, shift);
|
|
FEditorKey:=False;
|
|
{$ifdef dbgGrid}DebugLn('Grid.EditorKeyDown Key=',dbgs(Key),' END');{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorKeyPress(Sender: TObject; var Key: Char);
|
|
var
|
|
AChar: TUTF8Char;
|
|
{$ifdef dbgGrid}
|
|
function PrintKey:String;
|
|
begin
|
|
Result := Dbgs(ord(key))+' $' + IntToHex(ord(key),2);
|
|
if Key>#31 then
|
|
Result := Key + ' ' + Result
|
|
end;
|
|
{$endif}
|
|
begin
|
|
{$ifdef dbgGrid}DebugLn('Grid.EditorKeyPress: INIT Key=',PrintKey);{$Endif}
|
|
FEditorKey := True;
|
|
KeyPress(Key); // grid must get all keypresses, even if they are from the editor
|
|
{$ifdef dbgGrid}DebugLn('Grid.EditorKeyPress: inter Key=',PrintKey);{$Endif}
|
|
case Key of
|
|
#0, ^C,^V,^X:;
|
|
|
|
^M:
|
|
begin
|
|
Include(FGridFlags, gfEditingDone);
|
|
if not MoveNextAuto(GetKeyState(VK_SHIFT) < 0) then
|
|
ResetEditor;
|
|
Exclude(FGridFlags, gfEditingDone);
|
|
Key := #0;
|
|
end;
|
|
|
|
else begin
|
|
AChar := Key;
|
|
if not EditorCanAcceptKey(AChar) or EditorIsReadOnly then
|
|
Key := #0
|
|
else
|
|
Key := AChar[1];
|
|
end;
|
|
end;
|
|
FEditorKey := False;
|
|
{$ifdef dbgGrid}DebugLn('Grid.EditorKeyPress: END Key=',PrintKey);{$Endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char
|
|
);
|
|
begin
|
|
FEditorKey := True;
|
|
UTF8KeyPress(UTF8Key);
|
|
FEditorKey := false;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorKeyUp(Sender: TObject; var key: Word;
|
|
shift: TShiftState);
|
|
begin
|
|
FEditorKey := True;
|
|
KeyUp(Key, Shift);
|
|
FEditorKey := False;
|
|
end;
|
|
|
|
procedure TCustomGrid.SelectEditor;
|
|
var
|
|
aEditor: TWinControl;
|
|
begin
|
|
{$ifdef DbgGrid}
|
|
DebugLnEnter('TCustomGrid.SelectEditor INIT');
|
|
{$endif}
|
|
aEditor := GetDefaultEditor(Col);
|
|
if EditingAllowed(FCol) and Assigned(OnSelectEditor) then begin
|
|
// in some situations there are only non-selectable cells
|
|
// if goAlwaysShowEditor is on set initially editor to nil,
|
|
// user can modify this value in OnSelectEditor if needed
|
|
if not SelectCell(FCol,FRow) then
|
|
aEditor:=nil;
|
|
OnSelectEditor(Self, fCol, FRow, aEditor);
|
|
end;
|
|
if aEditor<>Editor then
|
|
Editor := aEditor;
|
|
if Assigned(Editor) and not Assigned(Editor.Popupmenu) then
|
|
Editor.PopupMenu := PopupMenu;
|
|
{$ifdef DbgGrid}
|
|
DebugLnExit('TCustomGrid.SelectEditor END');
|
|
{$endif}
|
|
end;
|
|
|
|
function TCustomGrid.EditorAlwaysShown: Boolean;
|
|
begin
|
|
Result:=EditingAllowed(FCol) and (goAlwaysShowEditor in Options) and not FixedGrid;
|
|
end;
|
|
|
|
//
|
|
procedure TCustomGrid.FixPosition(IsColumn: Boolean; aIndex: Integer);
|
|
var
|
|
OldCol,OldRow: Integer;
|
|
|
|
procedure FixSelection;
|
|
begin
|
|
if FRow > FRows.Count - 1 then
|
|
FRow := FRows.Count - 1
|
|
else if (FRow < FixedRows) and (FixedRows<FRows.Count) then
|
|
FRow := FixedRows;
|
|
if FCol > FCols.Count - 1 then
|
|
FCol := FCols.Count - 1
|
|
else if (FCol < FixedCols) and (FixedCols<FCols.Count) then
|
|
FCol := FixedCols;
|
|
end;
|
|
procedure FixTopLeft;
|
|
var
|
|
oldTL: TPoint;
|
|
VisCount: Integer;
|
|
begin
|
|
OldTL:=FTopLeft;
|
|
VisCount := FGCache.VisibleGrid.Right-FGCache.VisibleGrid.Left+1;
|
|
if OldTL.X+VisCount>FCols.Count then begin
|
|
OldTL.X := FCols.Count - VisCount;
|
|
if OldTL.X<FixedCols then
|
|
OldTL.X := FixedCols;
|
|
end;
|
|
VisCount := FGCache.VisibleGrid.Bottom-FGCache.VisibleGrid.Top+1;
|
|
if OldTL.Y+VisCount>FRows.Count then begin
|
|
OldTL.Y := FRows.Count - VisCount;
|
|
if OldTL.Y<FixedRows then
|
|
OldTL.Y:=FixedRows;
|
|
end;
|
|
if (OldTL <> FTopLeft) then begin
|
|
fTopLeft := OldTL;
|
|
//DebugLn('TCustomGrid.FixPosition ',DbgSName(Self),' FTopLeft=',dbgs(FTopLeft));
|
|
topleftChanged;
|
|
end;
|
|
end;
|
|
procedure FixEditor;
|
|
var
|
|
ColRow: Integer;
|
|
begin
|
|
if FixedGrid then begin
|
|
EditorMode:=False;
|
|
exit;
|
|
end;
|
|
if IsColumn then
|
|
ColRow:=OldCol
|
|
else
|
|
ColRow:=OldRow;
|
|
{$ifdef dbgeditor}
|
|
DebugLn('FixEditor: aIndex=%d ColRow=%d EditorMode=%s',[aIndex,ColRow,dbgs(EditorMode)]);
|
|
{$endif}
|
|
// Changed index is same as current colrow, new colrow may change
|
|
if AIndex=ColRow then begin
|
|
EditorMode:=False;
|
|
if EditorAlwaysShown then begin
|
|
SelectEditor;
|
|
EditorMode:=True;
|
|
end;
|
|
end else
|
|
// Changed index in before current colrow, just translate editor
|
|
if (AIndex<ColRow) and EditorMode then begin
|
|
if IsColumn then
|
|
AdjustEditorBounds(ColRow-1, OldRow)
|
|
else
|
|
AdjustEditorBounds(OldCol, ColRow-1)
|
|
end;
|
|
// else: changed index is after current colrow, it doesn't affect editor
|
|
end;
|
|
begin
|
|
OldCol := Col;
|
|
OldRow := Row;
|
|
FixTopleft;
|
|
FixSelection;
|
|
CheckPosition;
|
|
UpdateSelectionRange;
|
|
VisualChange;
|
|
FixEditor;
|
|
end;
|
|
|
|
procedure TCustomGrid.FixScroll;
|
|
var
|
|
OldColOffset: Integer;
|
|
OldTopLeft: TPoint;
|
|
begin
|
|
// TODO: fix rows too
|
|
// column handling
|
|
if FGCache.OldMaxTopLeft.x<>FGCache.MaxTopLeft.x then begin
|
|
// keeping FullVisibleGrid try to find a better topleft. We care are only
|
|
// if the grid is smaller than before, comparing GridWidth should work also
|
|
// but MaxTopLeft has better granularity
|
|
if FGCache.MaxTopLeft.x<FGCache.OldMaxTopLeft.x then begin
|
|
OldColOffset := FGCache.TLColOff;
|
|
OldTopLeft := fTopLeft;
|
|
FGCache.TLColOff := 0;
|
|
fTopleft.x := FixedCols;
|
|
if not ScrollToCell(FGCache.FullVisibleGrid.Right, Row, True) then begin
|
|
// target cell is now visible ....
|
|
if OldTopLeft.x<>fTopLeft.x then
|
|
// but the supposed startig left col is not the same as the current one
|
|
doTopleftChange(False)
|
|
else begin
|
|
FGCache.TLColOff := OldColOffset;
|
|
fTopLeft := OldTopLeft;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorShowChar(Ch: TUTF8Char);
|
|
begin
|
|
SelectEditor;
|
|
if FEDitor<>nil then begin
|
|
if EditorCanAcceptKey(ch) and not EditorIsReadOnly then begin
|
|
EditorShow(true);
|
|
TWSCustomGridClass(WidgetSetClass).SendCharToEditor(Editor, Ch);
|
|
//this method bypasses Self.KeyDown and therefore will not reset FRowAutoInserted there
|
|
//So, set it to false, unless pressing a backspace caused the editor to pop-up
|
|
if (Ch <> ^H) then FRowAutoInserted := False;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.EditorSetMode(const AValue: Boolean);
|
|
begin
|
|
{$ifdef dbgGrid}DebugLn('Grid.EditorSetMode=',dbgs(Avalue),' INIT');{$endif}
|
|
if not AValue then
|
|
EditorHide
|
|
else
|
|
EditorShow(false);
|
|
{$ifdef dbgGrid}DebugLn('Grid.EditorSetMode END');{$endif}
|
|
end;
|
|
|
|
function TCustomGrid.GetSelectedColor: TColor;
|
|
begin
|
|
Result:=FSelectedColor;
|
|
end;
|
|
|
|
function TCustomGrid.GetTitleShowPrefix(Column: Integer): boolean;
|
|
var
|
|
C: TGridColumn;
|
|
begin
|
|
C := ColumnFromGridColumn(Column);
|
|
if C<>nil then
|
|
result := C.Title.PrefixOption<>poNone
|
|
else
|
|
result := false;
|
|
end;
|
|
|
|
function TCustomGrid.GridColumnFromColumnIndex(ColumnIndex: Integer): Integer;
|
|
begin
|
|
{$ifdef NewCols}
|
|
result := ColumnIndex + FirstGridColumn;
|
|
if Result>ColCount-1 then
|
|
Result := -1;
|
|
{$else}
|
|
result := Columns.VisibleIndex(ColumnIndex);
|
|
if result>=0 then
|
|
result := result + FixedCols;
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TCustomGrid.GridMouseWheel(Shift: TShiftState; Delta: Integer);
|
|
begin
|
|
// Ctrl-key is to support horiz scrolling with basic mouse
|
|
if ssCtrl in Shift then
|
|
MoveNextSelectable(true, Delta, 0)
|
|
else
|
|
MoveNextSelectable(true, 0, Delta);
|
|
end;
|
|
|
|
function TCustomGrid.GetEditMask(ACol, ARow: Longint): string;
|
|
begin
|
|
result:='';
|
|
end;
|
|
|
|
function TCustomGrid.GetEditText(ACol, ARow: Longint): string;
|
|
begin
|
|
result:='';
|
|
end;
|
|
|
|
function TCustomGrid.GetColumnAlignment(Column: Integer; ForTitle: Boolean): TAlignment;
|
|
var
|
|
C: TGridColumn;
|
|
begin
|
|
C := ColumnFromGridColumn(Column);
|
|
if C<>nil then
|
|
if ForTitle then
|
|
Result := C.Title.Alignment
|
|
else
|
|
Result := C.Alignment
|
|
else
|
|
result := GetDefaultColumnAlignment(Column);
|
|
end;
|
|
|
|
function TCustomGrid.GetColumnColor(Column: Integer; ForTitle: Boolean): TColor;
|
|
var
|
|
C: TGridColumn;
|
|
begin
|
|
C := ColumnFromGridColumn(Column);
|
|
if C<>nil then
|
|
if ForTitle then
|
|
result := C.Title.Color
|
|
else
|
|
result := C.Color
|
|
else
|
|
if ForTitle then
|
|
result := FixedColor
|
|
else
|
|
result := Self.Color;
|
|
end;
|
|
|
|
function TCustomGrid.GetColumnFont(Column: Integer; ForTitle: Boolean): TFont;
|
|
var
|
|
C: TGridColumn;
|
|
begin
|
|
C := ColumnFromGridColumn(Column);
|
|
if C<>nil then
|
|
if ForTitle then
|
|
Result := C.Title.Font
|
|
else
|
|
Result := C.Font
|
|
else begin
|
|
if ForTitle then
|
|
Result := TitleFont
|
|
else
|
|
Result := Self.Font;
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.GetColumnLayout(Column: Integer; ForTitle: boolean): TTextLayout;
|
|
var
|
|
C: TGridColumn;
|
|
begin
|
|
C := ColumnFromGridColumn(Column);
|
|
if C<>nil then
|
|
if ForTitle then
|
|
Result := C.Title.Layout
|
|
else
|
|
Result := C.Layout
|
|
else
|
|
result := GetDefaultColumnLayout(Column);
|
|
end;
|
|
|
|
function TCustomGrid.GetColumnReadonly(Column: Integer): boolean;
|
|
var
|
|
C: TGridColumn;
|
|
begin
|
|
C := ColumnFromGridColumn(Column);
|
|
if C<>nil then
|
|
result := C.ReadOnly
|
|
else
|
|
result := GetDefaultColumnReadOnly(Column);
|
|
end;
|
|
|
|
function TCustomGrid.GetColumnTitle(Column: Integer): string;
|
|
var
|
|
C: TGridColumn;
|
|
begin
|
|
C := ColumnFromGridColumn(Column);
|
|
if C<>nil then
|
|
Result := C.Title.Caption
|
|
else
|
|
result := GetDefaultColumnTitle(Column);
|
|
end;
|
|
|
|
function TCustomGrid.GetColumnWidth(Column: Integer): Integer;
|
|
var
|
|
C: TGridColumn;
|
|
begin
|
|
C := ColumnFromGridColumn(Column);
|
|
if C<>nil then
|
|
Result := C.Width
|
|
else
|
|
Result := GetDefaultColumnWidth(Column);
|
|
end;
|
|
|
|
// return the relative cell coordinate of the next cell
|
|
// considering AutoAdvance property and selectable cells.
|
|
function TCustomGrid.GetDeltaMoveNext(const Inverse: boolean;
|
|
var ACol, ARow: Integer; const AAutoAdvance: TAutoAdvance): boolean;
|
|
var
|
|
|
|
DeltaCol,DeltaRow: Integer;
|
|
|
|
function CalcNextStep: boolean;
|
|
var
|
|
aa: TAutoAdvance;
|
|
cCol,cRow: Integer;
|
|
begin
|
|
|
|
DeltaCol := 0;
|
|
DeltaRow := 0;
|
|
|
|
aa := AAutoAdvance;
|
|
if Inverse then
|
|
case aa of
|
|
aaRight: aa := aaLeft;
|
|
aaLeft: aa := aaRight;
|
|
aaRightDown: aa := aaLeftUp;
|
|
aaLeftDown: aa := aaRightUp;
|
|
aaRightUP: aa := aaLeftDown;
|
|
aaLeftUP: aa := aaRightDown;
|
|
end;
|
|
|
|
case aa of
|
|
aaRight:
|
|
DeltaCol := 1;
|
|
|
|
aaLeft:
|
|
DeltaCol := -1;
|
|
|
|
aaDown:
|
|
DeltaRow := 1;
|
|
|
|
aaRightDown:
|
|
if ACol<ColCount-1 then
|
|
DeltaCol := 1
|
|
else begin
|
|
DeltaCol := FixedCols-ACol;
|
|
DeltaRow := 1;
|
|
end;
|
|
|
|
aaRightUP:
|
|
if ACol<ColCount-1 then
|
|
DeltaCol := 1
|
|
else begin
|
|
DeltaCol := FixedCols-ACol;
|
|
DeltaRow := -1;
|
|
end;
|
|
|
|
aaLeftUP:
|
|
if ACol>FixedCols then
|
|
DeltaCol := -1
|
|
else begin
|
|
DeltaCol := ColCount-1-ACol;
|
|
DeltaRow := -1;
|
|
end;
|
|
|
|
aaLeftDown:
|
|
if ACol>FixedCols then
|
|
DeltaCol := -1
|
|
else begin
|
|
DeltaCol := ColCount-1-ACol;
|
|
DeltaRow := 1;
|
|
end;
|
|
end;
|
|
|
|
CCol := ACol + DeltaCol;
|
|
CRow := ARow + DeltaRow;
|
|
|
|
// is CCol,CRow within range?
|
|
result :=
|
|
(CCol<=ColCount-1)and(CCol>=FixedCols)and
|
|
(CRow<=RowCount-1)and(CRow>=FixedRows);
|
|
end;
|
|
|
|
begin
|
|
ACol := FCol;
|
|
ARow := FRow;
|
|
|
|
result := False;
|
|
|
|
if AAutoAdvance=aaNone then begin
|
|
ACol := 0;
|
|
ARow := 0;
|
|
exit; // quick case, no auto movement allowed
|
|
end;
|
|
|
|
if [goRowSelect,goRelaxedRowSelect]*Options=[goRowSelect] then begin
|
|
if Inverse then
|
|
ACol := FixedCols
|
|
else
|
|
ACol := ColCount-1;
|
|
end;
|
|
|
|
// browse the grid in autoadvance order
|
|
while CalcNextStep do begin
|
|
ACol := ACol + DeltaCol;
|
|
ARow := ARow + DeltaRow;
|
|
// is cell ACol,ARow selectable?
|
|
result := SelectCell(ACol,ARow);
|
|
if Result then
|
|
break;
|
|
end;
|
|
|
|
if result then begin
|
|
// return relative position
|
|
ACol := ACol - FCol;
|
|
ARow := ARow - FRow;
|
|
end else begin
|
|
// no available next cell, return delta anyway
|
|
ACol := DeltaCol;
|
|
ARow := DeltaRow;
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.GetDefaultColumnAlignment(Column: Integer): TAlignment;
|
|
begin
|
|
result := DefaultTextStyle.Alignment;
|
|
end;
|
|
|
|
function TCustomGrid.GetDefaultEditor(Column: Integer): TWinControl;
|
|
var
|
|
C: TGridColumn;
|
|
bs: TColumnButtonStyle;
|
|
begin
|
|
result := nil;
|
|
if EditingAllowed(Col) then begin
|
|
C := ColumnFromGridColumn(Column);
|
|
if C<>nil then begin
|
|
bs := C.ButtonStyle;
|
|
if (bs=cbsAuto) and (C.PickList<>nil) and (C.PickList.Count>0) then
|
|
bs := cbsPicklist
|
|
end else
|
|
bs := cbsAuto;
|
|
|
|
result := EditorByStyle( Bs );
|
|
|
|
// by default do the editor setup here
|
|
// if user wants to change our setup, this can
|
|
// be done in OnSelectEditor
|
|
if (bs=cbsPickList) and (C<>nil) and (C.PickList<>nil) and
|
|
(result = FPicklistEditor) then begin
|
|
FPickListEditor.Items.Assign(C.PickList);
|
|
FPickListEditor.DropDownCount := C.DropDownRows;
|
|
end
|
|
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.GetDefaultRowHeight: integer;
|
|
var
|
|
TmpCanvas: TCanvas;
|
|
begin
|
|
tmpCanvas := GetWorkingCanvas(Canvas);
|
|
tmpCanvas.Font := Font;
|
|
tmpCanvas.Font.PixelsPerInch := Font.PixelsPerInch;
|
|
result := tmpCanvas.TextHeight('Fj')+7;
|
|
if tmpCanvas<>Canvas then
|
|
FreeWorkingCanvas(tmpCanvas);
|
|
end;
|
|
|
|
function TCustomGrid.GetGridDrawState(ACol, ARow: Integer): TGridDrawState;
|
|
begin
|
|
Result := [];
|
|
if ARow < FFixedRows then
|
|
include(Result, gdFixed)
|
|
else begin
|
|
if (aCol = FCol) and (aRow = FRow) then
|
|
Result := Result + [gdFocused, gdSelected]
|
|
else
|
|
if IsCellSelected[aCol, aRow] then
|
|
include(Result, gdSelected);
|
|
end;
|
|
if (aRow=FRow) and (goRowHighlight in FOptions) and not (gdFixed in Result) then
|
|
Result := Result + [gdRowHighlight];
|
|
with FGCache do begin
|
|
if (ACol = HotCell.x) and (ARow = HotCell.y) and not IsPushCellActive()
|
|
then Include(Result, gdHot);
|
|
if ClickCellPushed and (ACol = PushedCell.x) and (ARow = PushedCell.y)
|
|
then Include(Result, gdPushed);
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.GetScrollBarPosition(Which: integer): Integer;
|
|
var
|
|
ScrollInfo: TScrollInfo;
|
|
begin
|
|
if HandleAllocated then begin
|
|
ScrollInfo.cbSize := SizeOf(ScrollInfo);
|
|
ScrollInfo.fMask := SIF_POS;
|
|
GetScrollInfo(Handle, Which, ScrollInfo);
|
|
Result:=ScrollInfo.nPos;
|
|
end
|
|
else
|
|
Result:=0;
|
|
end;
|
|
|
|
function TCustomGrid.GetDefaultColumnWidth(Column: Integer): Integer;
|
|
begin
|
|
Result := FDefColWidth;
|
|
end;
|
|
|
|
function TCustomGrid.GetDefaultColumnLayout(Column: Integer): TTextLayout;
|
|
begin
|
|
result := DefaultTextStyle.Layout;
|
|
end;
|
|
|
|
function TCustomGrid.GetDefaultColumnReadOnly(Column: Integer): boolean;
|
|
begin
|
|
result := false;
|
|
end;
|
|
|
|
function TCustomGrid.GetDefaultColumnTitle(Column: Integer): string;
|
|
begin
|
|
result := '';
|
|
end;
|
|
|
|
procedure TCustomGrid.SetEditText(ACol, ARow: Longint; const Value: string);
|
|
begin
|
|
end;
|
|
|
|
function TCustomGrid.CanGridAcceptKey(Key: Word; Shift: TShiftState): Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetSelectedColor(const AValue: TColor);
|
|
begin
|
|
if FSelectedColor<>AValue then begin
|
|
FSelectedColor:=AValue;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetFadeUnfocusedSelection(const AValue: boolean);
|
|
begin
|
|
if FFadeUnfocusedSelection<>AValue then begin
|
|
FFadeUnfocusedSelection:=AValue;
|
|
if not Focused then
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SetFixedcolor(const AValue: TColor);
|
|
begin
|
|
if FFixedColor<>AValue then begin
|
|
FFixedColor:=Avalue;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
function TCustomGrid.GetFixedcolor: TColor;
|
|
begin
|
|
result:=FFixedColor;
|
|
end;
|
|
|
|
function TCustomGrid.GetFirstVisibleColumn: Integer;
|
|
begin
|
|
result := FixedCols;
|
|
while (result<ColCount) and (ColWidths[result]=0) do
|
|
inc(result); // extreme case may return colcount
|
|
end;
|
|
|
|
function TCustomGrid.GetFirstVisibleRow: Integer;
|
|
begin
|
|
result := FixedRows;
|
|
while (result<RowCount) and (RowHeights[result]=0) do
|
|
inc(result); // ditto
|
|
end;
|
|
|
|
function TCustomGrid.GetLastVisibleColumn: Integer;
|
|
begin
|
|
result := ColCount-1;
|
|
while (result>=0) and (ColWidths[result]=0) do
|
|
dec(result); // extreme case may return -1
|
|
end;
|
|
|
|
function TCustomGrid.GetLastVisibleRow: Integer;
|
|
begin
|
|
result := RowCount-1;
|
|
while (result>=0) and (RowHeights[result]=0) do
|
|
dec(result); // ditto
|
|
end;
|
|
|
|
procedure TCustomGrid.ColWidthsChanged;
|
|
begin
|
|
//
|
|
end;
|
|
procedure TCustomGrid.RowHeightsChanged;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
function TCustomGrid.RTLSign: Integer;
|
|
const
|
|
cRTLSign: array[TBiDiMode] of Integer = (1, -1, 1, 1);
|
|
begin
|
|
Result := cRTLSign[BiDiMode];
|
|
end;
|
|
|
|
procedure TCustomGrid.SaveColumns(cfg: TXMLConfig; Version: integer);
|
|
var
|
|
Path,cPath: string;
|
|
i: Integer;
|
|
c: TGridColumn;
|
|
begin
|
|
Path := 'grid/design/columns/';
|
|
cfg.SetValue(Path + 'columnsenabled', True);
|
|
cfg.SetValue(Path + 'columncount', columns.Count);
|
|
for i := 0 to columns.Count - 1 do begin
|
|
c := Columns[i];
|
|
cPath := Path + 'column' + IntToStr(i);
|
|
cfg.setValue(cPath + '/index/value', c.Index);
|
|
if c.IsWidthStored then
|
|
cfg.setValue(cPath + '/width/value', c.Width);
|
|
if c.IsMinSizeStored then cfg.SetValue(cPath + '/minsize/value', c.MinSize);
|
|
if c.IsMaxSizeStored then cfg.SetValue(cPath + '/maxsize/value', c.MaxSize);
|
|
if c.IsAlignmentStored then
|
|
cfg.setValue(cPath + '/alignment/value', ord(c.Alignment));
|
|
if c.IsLayoutStored then
|
|
cfg.setValue(cPath + '/layout/value', ord(c.Layout));
|
|
cfg.setValue(cPath + '/buttonstyle/value', ord(c.ButtonStyle));
|
|
if c.IsColorStored then
|
|
cfg.setValue(cPath + '/color/value', colortostring(c.Color));
|
|
if c.IsValueCheckedStored then
|
|
cfg.setValue(cPath + '/valuechecked/value', c.ValueChecked);
|
|
if c.IsValueUncheckedStored then
|
|
cfg.setValue(cPath + '/valueunchecked/value', c.ValueUnChecked);
|
|
if c.PickList.Count>0 then
|
|
cfg.SetValue(cPath + '/picklist/value', c.PickList.CommaText);
|
|
if c.IsSizePriorityStored then
|
|
cfg.SetValue(cPath + '/sizepriority/value', c.SizePriority);
|
|
if not c.IsDefaultFont then
|
|
CfgSetFontValue(cfg, cPath + '/font', c.Font);
|
|
cfg.setValue(cPath + '/title/caption/value', c.Title.Caption);
|
|
if not c.Title.IsDefaultFont then
|
|
CfgSetFontValue(cfg, cPath + '/title/font', c.Title.Font);
|
|
if c.Title.IsAlignmentStored then
|
|
cfg.setValue(cPath + '/title/alignment/value', ord(c.Title.Alignment));
|
|
if c.Title.IsColorStored then
|
|
cfg.setValue(cPath + '/title/color/value', colortostring(c.Title.Color));
|
|
if c.Title.IsLayoutStored then
|
|
cfg.setValue(cPath + '/title/layout/value', ord(c.Title.Layout));
|
|
|
|
doSaveColumn(self, c, -1, Cfg, Version, cPath);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SaveContent(cfg: TXMLConfig);
|
|
var
|
|
i,j,k: Integer;
|
|
Path, tmpPath: string;
|
|
begin
|
|
cfg.SetValue('grid/version', GRIDFILEVERSION);
|
|
|
|
Cfg.SetValue('grid/saveoptions/create', soDesign in SaveOptions);
|
|
if soDesign in SaveOptions then begin
|
|
Cfg.SetValue('grid/design/columncount', ColCount);
|
|
Cfg.SetValue('grid/design/rowcount', RowCount);
|
|
Cfg.SetValue('grid/design/fixedcols', FixedCols);
|
|
Cfg.SetValue('grid/design/fixedrows', Fixedrows);
|
|
Cfg.SetValue('grid/design/defaultcolwidth', DefaultColWidth);
|
|
Cfg.SetValue('grid/design/isdefaultcolwidth', ord(DefaultColWidthIsStored));
|
|
Cfg.SetValue('grid/design/defaultrowheight',DefaultRowHeight);
|
|
Cfg.SetValue('grid/design/isdefaultrowheight', ord(DefaultRowHeightIsStored));
|
|
Cfg.Setvalue('grid/design/color',ColorToString(Color));
|
|
|
|
if Columns.Enabled then
|
|
saveColumns(cfg, GRIDFILEVERSION)
|
|
else begin
|
|
j:=0;
|
|
for i:=0 to ColCount-1 do begin
|
|
k:=FCols[i];
|
|
if (k>=0)and(k<>DefaultColWidth) then begin
|
|
inc(j);
|
|
tmpPath := 'grid/design/columns/column'+IntToStr(j);
|
|
cfg.SetValue('grid/design/columns/columncount',j);
|
|
cfg.SetValue(tmpPath+'/index', i);
|
|
cfg.SetValue(tmpPath+'/width', k);
|
|
doSaveColumn(self, nil, i, Cfg, GRIDFILEVERSION, tmpPath);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
j:=0;
|
|
for i:=0 to RowCount-1 do begin
|
|
k:=FRows[i];
|
|
if (k>=0)and(k<>DefaultRowHeight) then begin
|
|
inc(j);
|
|
cfg.SetValue('grid/design/rows/rowcount',j);
|
|
cfg.SetValue('grid/design/rows/row'+IntToStr(j)+'/index', i);
|
|
cfg.SetValue('grid/design/rows/row'+IntToStr(j)+'/height',k);
|
|
end;
|
|
end;
|
|
|
|
SaveGridOptions(Cfg);
|
|
end;
|
|
|
|
Cfg.SetValue('grid/saveoptions/position', soPosition in SaveOptions);
|
|
if soPosition in SaveOptions then begin
|
|
Cfg.SetValue('grid/position/topleftcol',ftopleft.x);
|
|
Cfg.SetValue('grid/position/topleftrow',ftopleft.y);
|
|
Cfg.SetValue('grid/position/col',fCol);
|
|
Cfg.SetValue('grid/position/row',fRow);
|
|
if goRangeSelect in Options then begin
|
|
Cfg.SetValue('grid/position/selection/left',Selection.left);
|
|
Cfg.SetValue('grid/position/selection/top',Selection.top);
|
|
Cfg.SetValue('grid/position/selection/right',Selection.right);
|
|
Cfg.SetValue('grid/position/selection/bottom',Selection.bottom);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SaveGridOptions(cfg: TXMLConfig);
|
|
var
|
|
Path: string;
|
|
begin
|
|
Path:='grid/design/options/';
|
|
Cfg.SetValue(Path+'goFixedVertLine/value', goFixedVertLine in options);
|
|
Cfg.SetValue(Path+'goFixedHorzLine/value', goFixedHorzLine in options);
|
|
Cfg.SetValue(Path+'goVertLine/value', goVertLine in options);
|
|
Cfg.SetValue(Path+'goHorzLine/value', goHorzLine in options);
|
|
Cfg.SetValue(Path+'goRangeSelect/value', goRangeSelect in options);
|
|
Cfg.SetValue(Path+'goDrawFocusSelected/value', goDrawFocusSelected in options);
|
|
Cfg.SetValue(Path+'goRowSizing/value', goRowSizing in options);
|
|
Cfg.SetValue(Path+'goColSizing/value', goColSizing in options);
|
|
Cfg.SetValue(Path+'goRowMoving/value', goRowMoving in options);
|
|
Cfg.SetValue(Path+'goColMoving/value', goColMoving in options);
|
|
Cfg.SetValue(Path+'goEditing/value', goEditing in options);
|
|
Cfg.SetValue(Path+'goAutoAddRows/value', goAutoAddRows in options);
|
|
Cfg.SetValue(Path+'goTabs/value', goTabs in options);
|
|
Cfg.SetValue(Path+'goRowSelect/value', goRowSelect in options);
|
|
Cfg.SetValue(Path+'goAlwaysShowEditor/value', goAlwaysShowEditor in options);
|
|
Cfg.SetValue(Path+'goThumbTracking/value', goThumbTracking in options);
|
|
Cfg.SetValue(Path+'goColSpanning/value', goColSpanning in options);
|
|
cfg.SetValue(Path+'goRelaxedRowSelect/value', goRelaxedRowSelect in options);
|
|
cfg.SetValue(Path+'goDblClickAutoSize/value', goDblClickAutoSize in options);
|
|
Cfg.SetValue(Path+'goSmoothScroll/value', goSmoothScroll in Options);
|
|
Cfg.SetValue(Path+'goAutoAddRowsSkipContentCheck/value', goAutoAddRowsSkipContentCheck in Options);
|
|
Cfg.SetValue(Path+'goRowHighlight/value', goRowHighlight in Options);
|
|
Cfg.SetValue(Path+'goScrollToLastCol/value', goScrollToLastCol in Options2);
|
|
Cfg.SetValue(Path+'goScrollToLastRow/value', goScrollToLastRow in Options2);
|
|
end;
|
|
|
|
procedure TCustomGrid.LoadColumns(cfg: TXMLConfig; Version: integer);
|
|
var
|
|
i, k: integer;
|
|
path, cPath, s: string;
|
|
c: TGridColumn;
|
|
begin
|
|
Path := 'grid/design/columns/';
|
|
k := cfg.getValue(Path + 'columncount', 0);
|
|
for i := 0 to k - 1 do
|
|
Columns.Add;
|
|
for i := 0 to k - 1 do begin
|
|
c := Columns[i];
|
|
cPath := Path + 'column' + IntToStr(i);
|
|
c.index := cfg.getValue(cPath + '/index/value', i);
|
|
s := cfg.GetValue(cPath + '/width/value', '');
|
|
if s<>'' then c.Width := StrToIntDef(s, DEFCOLWIDTH);
|
|
c.MinSize := cfg.GetValue(cPath + '/minsize/value', DEFMINSIZE);
|
|
c.MaxSize := cfg.GetValue(cPath + '/maxsize/value', DEFMAXSIZE);
|
|
s := cfg.getValue(cPath + '/alignment/value', '');
|
|
if s<>'' then
|
|
c.Alignment := TAlignment(StrToIntDef(s, 0));
|
|
s := cfg.GetValue(cPath + '/layout/value', '');
|
|
if s<>'' then
|
|
c.Layout := TTextLayout(StrToIntDef(s, 0));
|
|
s := cfg.getValue(cPath + '/buttonstyle/value', '0');
|
|
c.ButtonStyle := TColumnButtonStyle(StrToInt(s));
|
|
s := cfg.getValue(cPath + '/color/value', '');
|
|
if s<>'' then
|
|
c.Color := StringToColor(s);
|
|
s := cfg.getValue(cPath + '/valuechecked/value', '');
|
|
if s<>'' then
|
|
c.ValueChecked := s;
|
|
s := cfg.getValue(cPath + '/valueunchecked/value', '');
|
|
if s<>'' then
|
|
c.ValueUnChecked := s;
|
|
s := cfg.GetValue(cPath + '/picklist/value', '');
|
|
if s<>'' then
|
|
c.PickList.CommaText := s;
|
|
s := cfg.GetValue(cPath + '/sizepriority/value', '');
|
|
if s<>'' then
|
|
c.SizePriority := StrToIntDef(s, 0);
|
|
s := cfg.GetValue(cPath + '/font/name/value', '');
|
|
if s<>'' then
|
|
cfgGetFontValue(cfg, cPath + '/font', c.Font);
|
|
c.Title.Caption := cfg.getValue(cPath + '/title/caption/value', 'title ' + IntToStr(i));
|
|
s := cfg.GetValue(cPath + '/title/font/name/value', '');
|
|
if s<>'' then
|
|
cfgGetFontValue(cfg, cPath + '/title/font', c.Title.Font);
|
|
s := cfg.getValue(cPath + '/title/alignment/value', '');
|
|
if s<>'' then
|
|
c.Title.Alignment := TAlignment(StrToIntDef(s, 0));
|
|
s := cfg.getValue(cPath + '/title/color/value', '');
|
|
if s<>'' then
|
|
c.Title.Color := StringToColor(s);
|
|
s := cfg.GetValue(cPath + 'title/layout/value', '');
|
|
if s<>'' then
|
|
c.Title.Layout := TTextLayout(StrToIntDef(s, 0));
|
|
|
|
doLoadColumn(self, c, -1, cfg, version, cpath);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TCustomGrid.LoadContent(cfg: TXMLConfig; Version: Integer);
|
|
var
|
|
CreateSaved: Boolean;
|
|
i,j,k: Integer;
|
|
Path, tmpPath: string;
|
|
begin
|
|
if soDesign in FSaveOptions then begin
|
|
CreateSaved:=Cfg.GetValue('grid/saveoptions/create', false);
|
|
if CreateSaved then begin
|
|
Clear;
|
|
Columns.Clear;
|
|
FixedCols:=0;
|
|
FixedRows:=0;
|
|
|
|
if cfg.getValue('grid/design/columns/columnsenabled', False) then
|
|
LoadColumns(cfg, version)
|
|
else
|
|
ColCount := Cfg.GetValue('grid/design/columncount', 5);
|
|
|
|
RowCount:=Cfg.GetValue('grid/design/rowcount', 5);
|
|
FixedCols:=Cfg.GetValue('grid/design/fixedcols', 1);
|
|
FixedRows:=Cfg.GetValue('grid/design/fixedrows', 1);
|
|
|
|
k := Cfg.GetValue('grid/design/isdefaultrowheight', -1);
|
|
if k<>0 then
|
|
DefaultRowheight:=Cfg.GetValue('grid/design/defaultrowheight', -1)
|
|
else
|
|
DefaultRowheight:=-1;
|
|
|
|
k := Cfg.GetValue('grid/design/isdefaultcolwidth', -1);
|
|
if k<>0 then
|
|
DefaultColWidth:=Cfg.getValue('grid/design/defaultcolwidth', -1)
|
|
else
|
|
DefaultColWidth:=-1;
|
|
|
|
try
|
|
Color := StringToColor(cfg.GetValue('grid/design/color', 'clWindow'));
|
|
except
|
|
end;
|
|
|
|
if not Columns.Enabled then begin
|
|
Path:='grid/design/columns/';
|
|
k:=cfg.getValue(Path+'columncount',0);
|
|
for i:=1 to k do begin
|
|
tmpPath := Path+'column'+IntToStr(i);
|
|
j:=cfg.getValue(tmpPath+'/index',-1);
|
|
if IsColumnIndexValid(j) then begin
|
|
ColWidths[j]:=cfg.getValue(tmpPath+'/width',-1);
|
|
doLoadColumn(self, nil, j, Cfg, Version, tmpPath);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Path:='grid/design/rows/';
|
|
k:=cfg.getValue(Path+'rowcount',0);
|
|
for i:=1 to k do begin
|
|
j:=cfg.getValue(Path+'row'+IntToStr(i)+'/index',-1);
|
|
if IsRowIndexValid(j) then begin
|
|
RowHeights[j]:=cfg.getValue(Path+'row'+IntToStr(i)+'/height',-1);
|
|
end;
|
|
end;
|
|
|
|
LoadGridOptions(cfg, Version);
|
|
end;
|
|
|
|
CreateSaved:=Cfg.GetValue('grid/saveoptions/position', false);
|
|
if CreateSaved then begin
|
|
i:=Cfg.GetValue('grid/position/topleftcol',-1);
|
|
j:=Cfg.GetValue('grid/position/topleftrow',-1);
|
|
if CellToGridZone(i,j)=gzNormal then begin
|
|
TryScrollTo(i,j,True,True);
|
|
end;
|
|
i:=Cfg.GetValue('grid/position/col',-1);
|
|
j:=Cfg.GetValue('grid/position/row',-1);
|
|
if IsColumnIndexVariable(i) and
|
|
IsRowIndexVariable(j) then begin
|
|
MoveExtend(false, i,j, True);
|
|
end;
|
|
if goRangeSelect in Options then begin
|
|
FRange.left:=Cfg.getValue('grid/position/selection/left',FCol);
|
|
FRange.Top:=Cfg.getValue('grid/position/selection/top',FRow);
|
|
FRange.Right:=Cfg.getValue('grid/position/selection/right',FCol);
|
|
FRange.Bottom:=Cfg.getValue('grid/position/selection/bottom',FRow);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.LoadGridOptions(cfg: TXMLConfig; Version: Integer);
|
|
var
|
|
Opt: TGridOptions;
|
|
Opt2: TGridOptions2;
|
|
Path: string;
|
|
|
|
procedure GetValue(optStr:string; aOpt:TGridOption);
|
|
begin
|
|
if Cfg.GetValue(Path+OptStr+'/value', False) then Opt:=Opt+[aOpt];
|
|
end;
|
|
procedure GetValue2(optStr:string; aOpt:TGridOption2);
|
|
begin
|
|
if Cfg.GetValue(Path+OptStr+'/value', False) then Opt2:=Opt2+[aOpt];
|
|
end;
|
|
begin
|
|
Opt:=[];
|
|
Opt2:=[];
|
|
Path:='grid/design/options/';
|
|
GetValue('goFixedVertLine', goFixedVertLine);
|
|
GetValue('goFixedHorzLine', goFixedHorzLine);
|
|
GetValue('goVertLine',goVertLine);
|
|
GetValue('goHorzLine',goHorzLine);
|
|
GetValue('goRangeSelect',goRangeSelect);
|
|
GetValue('goDrawFocusSelected',goDrawFocusSelected);
|
|
GetValue('goRowSizing',goRowSizing);
|
|
GetValue('goColSizing',goColSizing);
|
|
GetValue('goRowMoving',goRowMoving);
|
|
GetValue('goColMoving',goColMoving);
|
|
GetValue('goEditing',goEditing);
|
|
GetValue('goAutoAddRows',goAutoAddRows);
|
|
GetValue('goRowSelect',goRowSelect);
|
|
GetValue('goTabs',goTabs);
|
|
GetValue('goAlwaysShowEditor',goAlwaysShowEditor);
|
|
GetValue('goThumbTracking',goThumbTracking);
|
|
GetValue('goColSpanning', goColSpanning);
|
|
GetValue('goRelaxedRowSelect',goRelaxedRowSelect);
|
|
GetValue('goDblClickAutoSize',goDblClickAutoSize);
|
|
GetValue('goAutoAddRowsSkipContentCheck',goAutoAddRowsSkipContentCheck);
|
|
GetValue('goRowHighlight',goRowHighlight);
|
|
if Version>=2 then begin
|
|
GetValue('goSmoothScroll',goSmoothScroll);
|
|
end;
|
|
GetValue2('goScrollToLastRow',goScrollToLastRow);
|
|
GetValue2('goScrollToLastCol',goScrollToLastCol);
|
|
|
|
Options:=Opt;
|
|
Options2:=Opt2;
|
|
end;
|
|
|
|
procedure TCustomGrid.Loaded;
|
|
begin
|
|
inherited Loaded;
|
|
VisualChange;
|
|
end;
|
|
|
|
procedure TCustomGrid.LockEditor;
|
|
begin
|
|
inc(FEditorHidingCount);
|
|
{$ifdef dbgGrid}DebugLn('==> LockEditor: ', dbgs(FEditorHidingCount)); {$endif}
|
|
end;
|
|
|
|
constructor TCustomGrid.Create(AOwner: TComponent);
|
|
begin
|
|
// Inherited create Calls SetBounds->WM_SIZE->VisualChange so
|
|
// fGrid needs to be created before that
|
|
FCols:=TIntegerList.Create;
|
|
FRows:=TIntegerList.Create;
|
|
FGCache.AccumWidth:=TIntegerList.Create;
|
|
FGCache.AccumHeight:=TIntegerList.Create;
|
|
FGCache.ClickCell := point(-1, -1);
|
|
inherited Create(AOwner);
|
|
|
|
FVSbVisible := -1;
|
|
FHSbVisible := -1;
|
|
|
|
FColumns := CreateColumns;
|
|
|
|
FTitleFont := TFont.Create;
|
|
FTitleFont.OnChange := @OnTitleFontChanged;
|
|
FTitleFontIsDefault := True;
|
|
|
|
FAutoAdvance := aaRight;
|
|
FTabAdvance := aaRightDown;
|
|
FAutoEdit := True;
|
|
FFocusRectVisible := True;
|
|
FDefaultDrawing := True;
|
|
FOptions:=
|
|
[goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect,
|
|
goSmoothScroll ];
|
|
FScrollbars:=ssAutoBoth;
|
|
fGridState:=gsNormal;
|
|
FDefColWidth:=-1;
|
|
FDefRowHeight:=-1;
|
|
FGridLineColor:=clSilver;
|
|
FFixedGridLineColor := cl3DDKShadow;
|
|
FGridLineStyle:=psSolid;
|
|
FGridLineWidth := 1;
|
|
fFocusColor:=clRed;
|
|
FFixedColor:=clBtnFace;
|
|
FFixedHotColor:=cl3DLight;
|
|
FSelectedColor:= clHighlight;
|
|
FFadeUnfocusedSelection:=false;
|
|
FDisabledFontColor:=clGrayText;
|
|
FRange:=Rect(-1,-1,-1,-1);
|
|
FDragDx:=3;
|
|
SetBounds(0,0,200,100);
|
|
ColCount:=5;
|
|
RowCount:=5;
|
|
FixedCols:=1;
|
|
FixedRows:=1;
|
|
Editor:=nil;
|
|
FBorderColor := cl3DDKShadow;
|
|
FGridBorderStyle := bsSingle;
|
|
UpdateBorderStyle;
|
|
FIgnoreClick := False;
|
|
|
|
ParentColor := False;
|
|
Color:=clWindow;
|
|
FAlternateColor := Color;
|
|
FAltColorStartNormal := true;
|
|
|
|
FDefaultTextStyle := Canvas.TextStyle;
|
|
FDefaultTextStyle.Wordbreak := False;
|
|
FDefaultTextStyle.SingleLine:= True;
|
|
|
|
FCellHintPriority := chpAllNoDefault;
|
|
|
|
FButtonEditor := TButtonCellEditor.Create(nil);
|
|
FButtonEditor.Name:='ButtonEditor';
|
|
FButtonEditor.Caption:='...';
|
|
FButtonEditor.Visible:=False;
|
|
FButtonEditor.Width:=25;
|
|
FButtonEditor.OnClick := @EditButtonClicked;
|
|
|
|
FStringEditor := TStringCellEditor.Create(nil);
|
|
FStringEditor.name :='StringEditor';
|
|
FStringEditor.Text:='';
|
|
FStringEditor.Visible:=False;
|
|
FStringEditor.Align:=alNone;
|
|
FStringEditor.BorderStyle := bsNone;
|
|
|
|
FPicklistEditor := TPickListCellEditor.Create(nil);
|
|
FPickListEditor.Name := 'PickListEditor';
|
|
FPickListEditor.Visible := False;
|
|
FPickListEditor.AutoSize := false;
|
|
|
|
FButtonStringEditor := TCompositeCellEditor.Create(nil);
|
|
FButtonStringEditor.Name:='ButtonTextEditor';
|
|
FButtonStringEditor.Visible:=False;
|
|
FButtonStringEditor.AddEditor(FStringEditor, alCustom, true);
|
|
FButtonStringEditor.AddEditor(FButtonEditor, alRight, false);
|
|
|
|
FFastEditing := True;
|
|
TabStop := True;
|
|
FAllowOutboundEvents:=True;
|
|
|
|
FHeaderHotZones := [gzFixedCols];
|
|
FHeaderPushZones := [gzFixedCols];
|
|
ResetHotCell;
|
|
ResetPushedCell;
|
|
FSortOrder := soAscending;
|
|
FSortColumn:=-1;
|
|
FAscImgInd:=-1;
|
|
FDescImgInd:=-1;
|
|
|
|
FValidateOnSetSelection := false;
|
|
|
|
FColRowDragIndicatorColor := clRed;
|
|
|
|
FSpecialCursors[gcsColWidthChanging] := crHSplit;
|
|
FSpecialCursors[gcsRowHeightChanging] := crVSplit;
|
|
FSpecialCursors[gcsDragging] := crMultiDrag;
|
|
|
|
varRubberSpace := Scale96ToScreen(constRubberSpace);
|
|
varCellPadding := Scale96ToScreen(constCellPadding);
|
|
varColRowBorderTolerance := Scale96ToScreen(constColRowBorderTolerance);
|
|
end;
|
|
|
|
destructor TCustomGrid.Destroy;
|
|
begin
|
|
{$Ifdef DbgGrid}DebugLn('TCustomGrid.Destroy');{$Endif}
|
|
FreeThenNil(FButtonStringEditor);
|
|
FreeThenNil(FPickListEditor);
|
|
FreeThenNil(FStringEditor);
|
|
FreeThenNil(FButtonEditor);
|
|
FreeThenNil(FColumns);
|
|
FreeThenNil(FGCache.AccumWidth);
|
|
FreeThenNil(FGCache.AccumHeight);
|
|
FreeThenNil(FCols);
|
|
FreeThenNil(FRows);
|
|
FreeThenNil(FTitleFont);
|
|
FEditor := nil;
|
|
FreeAndNil(FScroller);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCustomGrid.LoadSub(ACfg: TXMLConfig);
|
|
var
|
|
Version: Integer;
|
|
begin
|
|
Version:=ACfg.GetValue('grid/version',-1);
|
|
if Version=-1 then raise Exception.Create(rsNotAValidGridFile);
|
|
BeginUpdate;
|
|
try
|
|
LoadContent(ACfg, Version);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.LoadFromFile(FileName: string);
|
|
var
|
|
Cfg: TXMLConfig;
|
|
begin
|
|
if not FileExistsUTF8(FileName) then
|
|
raise Exception.Create(rsGridFileDoesNotExist);
|
|
Cfg:=TXMLConfig.Create(nil);
|
|
Try
|
|
Cfg.Filename := FileName;
|
|
LoadSub(Cfg);
|
|
Finally
|
|
FreeThenNil(Cfg);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.LoadFromStream(AStream: TStream);
|
|
var
|
|
Cfg: TXMLConfig;
|
|
begin
|
|
Cfg:=TXMLConfig.Create(nil);
|
|
Try
|
|
Cfg.ReadFromStream(AStream);
|
|
LoadSub(Cfg);
|
|
Finally
|
|
FreeThenNil(Cfg);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SaveToFile(FileName: string);
|
|
var
|
|
Cfg: TXMLConfig;
|
|
begin
|
|
if FileExistsUTF8(FileName) then
|
|
DeleteFileUTF8(FileName);
|
|
Cfg:=TXMLConfig.Create(nil);
|
|
Try
|
|
Cfg.FileName := FileName;
|
|
SaveContent(Cfg);
|
|
Cfg.Flush;
|
|
Finally
|
|
FreeThenNil(Cfg);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.SaveToStream(AStream: TStream);
|
|
var
|
|
Cfg: TXMLConfig;
|
|
begin
|
|
Cfg:=TXMLConfig.Create(nil);
|
|
Try
|
|
Cfg.Clear;
|
|
SaveContent(Cfg);
|
|
Cfg.WriteToStream(AStream);
|
|
Finally
|
|
FreeThenNil(Cfg);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.ScaleFontsPPI(const AToPPI: Integer; const AProportion: Double);
|
|
var
|
|
LTitleFontIsDefault: Boolean;
|
|
I: Integer;
|
|
begin
|
|
inherited ScaleFontsPPI(AToPPI, AProportion);
|
|
LTitleFontIsDefault := FTitleFontIsDefault;
|
|
DoScaleFontPPI(TitleFont, AToPPI, AProportion);
|
|
FTitleFontIsDefault := LTitleFontIsDefault;
|
|
for I := 0 to FColumns.Count-1 do
|
|
FColumns[I].ScaleFontsPPI(AToPPI, AProportion);
|
|
end;
|
|
|
|
type
|
|
TWinCtrlAccess=class(TWinControl);
|
|
|
|
procedure TCustomGrid.SetFocus;
|
|
var
|
|
NextControl: TWinControl;
|
|
ParentForm: TCustomForm;
|
|
ForwardTab: boolean;
|
|
begin
|
|
{$IFDEF dbgGrid}
|
|
DebugLnEnter('TCustomGrid.SetFocus INIT.');
|
|
{$ENDIF}
|
|
if (Editor<>nil) and Editor.Focused and
|
|
([gfEditorTab,gfRevEditorTab]*GridFlags<>[]) then begin
|
|
// Editor was doing TAB. Focus next control instead
|
|
ForwardTab:= gfEditorTab in GridFlags;
|
|
GridFlags:=GridFlags-[gfEditorTab,gfRevEditorTab];
|
|
ParentForm:=GetParentForm(Self);
|
|
if ParentForm<>nil then begin
|
|
NextControl:=TWinCtrlAccess(Pointer(ParentForm)).FindNextControl(Self,
|
|
ForwardTab, true, false);
|
|
if NextControl<>nil then begin
|
|
{$IFDEF dbgGrid}
|
|
DebugLn('Was tabbing, will focus: ',dbgsname(NextControl));
|
|
{$ENDIF}
|
|
if (NextControl<>Self) and (NextControl<>Editor) then begin
|
|
NextControl.SetFocus;
|
|
{$ifdef DbgGrid}
|
|
DebugLnExit('Skipping inherited, EXIT');
|
|
{$endif}
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if (Editor <> nil) and (Editor.Visible) then
|
|
Editor.SetFocus
|
|
else
|
|
inherited SetFocus;
|
|
|
|
{$IFDEF dbgGrid}
|
|
DebugLnExit('TCustomGrid.SetFocus END');
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{$ifdef WINDOWS}
|
|
// editor focusing make bad on IME input.
|
|
procedure TCustomGrid.IMEStartComposition(var Msg: TMessage);
|
|
begin
|
|
EditorSetValue;
|
|
if EditingAllowed(FCol) and CanEditShow and (not FEditorShowing) and
|
|
(Editor<>nil) and (not Editor.Visible) and (not EditorLocked) then
|
|
begin
|
|
// prepare IME input on Editor
|
|
Editor.Visible:=True;
|
|
FEditorOldValue := GetCells(FCol,FRow);
|
|
EditorSelectAll;
|
|
FGridState := gsNormal;
|
|
Editor.Dispatch(Msg);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGrid.IMEComposition(var Msg: TMessage);
|
|
begin
|
|
if EditingAllowed(FCol) and CanEditShow and (not FEditorShowing) and
|
|
(Editor<>nil) and (not Editor.Visible) and (not EditorLocked) then
|
|
Editor.Dispatch(Msg);
|
|
end;
|
|
|
|
procedure TCustomGrid.IMEEndComposition(var Msg: TMessage);
|
|
begin
|
|
if EditingAllowed(FCol) and CanEditShow and (not FEditorShowing) and
|
|
(Editor<>nil) and (not Editor.Visible) and (not EditorLocked) then
|
|
Editor.Dispatch(Msg);
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
function TCustomGrid.ClearCols: Boolean;
|
|
begin
|
|
Result:=False;
|
|
if FCols.Count=0 then
|
|
exit; // already cleared
|
|
if EditorMode then
|
|
EditorMode:=False;
|
|
// save some properties
|
|
FGridPropBackup.FixedColCount := FFixedCols;
|
|
FGridPropBackup.ColCount := ColCount;
|
|
// clear structure
|
|
FFixedCols:=0;
|
|
FCols.Count:=0;
|
|
FGCache.TLColOff := 0;
|
|
Result:=True;
|
|
end;
|
|
|
|
function TCustomGrid.ClearRows: Boolean;
|
|
begin
|
|
Result:=False;
|
|
if FRows.Count=0 then
|
|
exit; // already cleared
|
|
if EditorMode then
|
|
EditorMode:=False;
|
|
// save some properties
|
|
FGridPropBackup.FixedRowCount := FFixedRows;
|
|
FGridPropBackup.RowCount := RowCount;
|
|
// clear structure
|
|
FFixedRows:=0;
|
|
FRows.Count:=0;
|
|
FGCache.TlRowOff := 0;
|
|
Result:=True;
|
|
end;
|
|
|
|
procedure TCustomGrid.Clear;
|
|
var
|
|
OldR,OldC: Integer;
|
|
RowChanged, ColChanged: Boolean;
|
|
begin
|
|
if EditorMode then
|
|
EditorMode := false;
|
|
OldR:=RowCount;
|
|
OldC:=ColCount;
|
|
RowChanged := ClearRows;
|
|
ColChanged := ClearCols;
|
|
if not (RowChanged or ColChanged) then
|
|
exit; // already cleared
|
|
FGridPropBackup.ValidData := True;
|
|
FTopLeft:=Point(-1,-1);
|
|
FRange:=Rect(-1,-1,-1,-1);
|
|
FGCache.HotCellPainted := false;
|
|
ResetHotCell;
|
|
VisualChange;
|
|
SizeChanged(OldR,OldC);
|
|
end;
|
|
|
|
procedure TCustomGrid.AutoAdjustColumns;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
For i:=0 to ColCount-1 do
|
|
AutoAdjustColumn(i);
|
|
end;
|
|
|
|
{ TVirtualGrid }
|
|
|
|
function TVirtualGrid.GetCells(Col, Row: Integer): PCellProps;
|
|
begin
|
|
// todo: Check range
|
|
Result:=nil;
|
|
if not IsColumnIndexValid(Col) or not IsRowIndexValid(Row) then
|
|
raise EGridException.CreateFmt(rsIndexOutOfRange, [Col, Row]);
|
|
Result:=FCellArr[Col,Row];
|
|
end;
|
|
|
|
function TVirtualGrid.GetRows(Row: Integer): PColRowProps;
|
|
begin
|
|
Result:= FRowArr[Row, 0];
|
|
end;
|
|
|
|
function TVirtualGrid.GetCols(Col: Integer): PColRowProps;
|
|
begin
|
|
result:=FColArr[Col, 0];
|
|
end;
|
|
|
|
procedure TVirtualGrid.SetCells(Col, Row: Integer; const AValue: PCellProps);
|
|
var
|
|
Cell: PCellProps;
|
|
begin
|
|
// todo: Check range
|
|
Cell:=FCellArr[Col,Row];
|
|
if Cell<>nil then
|
|
DisposeCell(Cell);
|
|
Cell:=AValue;
|
|
FCellArr[Col,Row]:=Cell;
|
|
end;
|
|
|
|
procedure TVirtualGrid.SetRows(Row: Integer; const Avalue: PColRowProps);
|
|
var
|
|
C: PColRowProps;
|
|
begin
|
|
// todo: Check range
|
|
C:=FRowArr[Row,0];
|
|
if C<>nil then DisposeColRow(C);
|
|
FRowArr[Row,0]:=AValue;
|
|
end;
|
|
|
|
procedure TVirtualGrid.SetColCount(const Avalue: Integer);
|
|
begin
|
|
if FColCount=Avalue then Exit;
|
|
{$Ifdef dbgMem}
|
|
DebugLn('TVirtualGrid.SetColCount Value=',AValue);
|
|
{$Endif}
|
|
FColCount:=AValue;
|
|
{$Ifdef dbgMem}
|
|
DBGOut('TVirtualGrid.SetColCount->FCOLS: ');
|
|
{$Endif}
|
|
FColArr.SetLength(FColCount, 1);
|
|
{$Ifdef dbgMem}
|
|
DBGOut('TVirtualGrid.SetColCount->FCELLS(',FColCount,',',FRowCount,'): ');
|
|
{$Endif}
|
|
FCellArr.SetLength(FColCount, FRowCount);
|
|
end;
|
|
|
|
|
|
procedure TVirtualGrid.SetRowCount(const Avalue: Integer);
|
|
begin
|
|
if FRowCount=AValue then Exit;
|
|
{$Ifdef dbgMem}
|
|
DebugLn('TVirtualGrid.SetRowCount Value=',AValue);
|
|
{$Endif}
|
|
FRowCount:=AValue;
|
|
{$Ifdef dbgMem}
|
|
DBGOut('TVirtualGrid.SetRowCount->FROWS: ');
|
|
{$Endif}
|
|
FRowArr.SetLength(FRowCount,1);
|
|
{$Ifdef dbgMem}
|
|
DBGOut('TVirtualGrid.SetRowCount->FCELLS(',FColCount,',',FRowCount,'): ');
|
|
{$Endif}
|
|
FCellArr.SetLength(FColCount, FRowCount);
|
|
end;
|
|
|
|
procedure TVirtualGrid.SetCols(Col: Integer; const Avalue: PColRowProps);
|
|
var
|
|
C: PColRowProps;
|
|
begin
|
|
// todo: Check range
|
|
C:=FColArr[Col,0];
|
|
if C<>nil then DisposeColRow(C);
|
|
FColArr[Col,0]:=AValue;
|
|
end;
|
|
|
|
procedure TVirtualGrid.Clear;
|
|
begin
|
|
{$Ifdef dbgMem}DBGOut('FROWARR: ');{$Endif}FRowArr.Clear;
|
|
{$Ifdef dbgMem}DBGOut('FCOLARR: ');{$Endif}FColArr.Clear;
|
|
{$Ifdef dbgMem}DBGOut('FCELLARR: ');{$Endif}FCellArr.Clear;
|
|
FColCount:=0;
|
|
FRowCount:=0;
|
|
end;
|
|
|
|
procedure TVirtualGrid.DisposeCell(var P: PCellProps);
|
|
begin
|
|
if P<>nil then begin
|
|
if P^.Text<>nil then StrDispose(P^.Text);
|
|
Dispose(P);
|
|
P:=nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TVirtualGrid.DisposeColRow(var p: PColRowProps);
|
|
begin
|
|
if P<>nil then begin
|
|
Dispose(P);
|
|
P:=nil;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualGrid.IsColumnIndexValid(AIndex: Integer): boolean;
|
|
begin
|
|
Result := (AIndex>=0) and (AIndex<ColCount);
|
|
end;
|
|
|
|
function TVirtualGrid.IsRowIndexValid(AIndex: Integer): boolean;
|
|
begin
|
|
Result := (AIndex>=0) and (AIndex<RowCount);
|
|
end;
|
|
|
|
function TVirtualGrid.GetDefaultCell: PcellProps;
|
|
begin
|
|
New(Result);
|
|
Result^.Text:=nil;
|
|
Result^.Attr:=nil;
|
|
end;
|
|
|
|
function TVirtualGrid.GetDefaultColRow: PColRowProps;
|
|
begin
|
|
New(Result);
|
|
Result^.FixedAttr:=nil;
|
|
Result^.NormalAttr:=nil;
|
|
Result^.Size:=-1;
|
|
end;
|
|
|
|
procedure TVirtualGrid.doDestroyItem(Sender: TObject; Col,Row: Integer;
|
|
var Item: Pointer);
|
|
begin
|
|
{$Ifdef dbgMem}
|
|
DebugLn('TVirtualGrid.doDestroyItem Col=',Col,' Row= ',
|
|
Row,' Item=',Integer(Item));
|
|
{$endif}
|
|
if Item<>nil then begin
|
|
if (Sender=FColArr)or(Sender=FRowArr) then begin
|
|
DisposeColRow(PColRowProps(Item));
|
|
end else begin
|
|
DisposeCell(PCellProps(Item));
|
|
end;
|
|
Item:=nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TVirtualGrid.doNewItem(Sender: TObject; Col,Row: Integer;
|
|
var Item: Pointer);
|
|
begin
|
|
{$Ifdef dbgMem}
|
|
DebugLn('TVirtualGrid.doNewItem Col=',Col,' Row= ',
|
|
Row,' Item=',Integer(Item));
|
|
{$endif}
|
|
if Sender=FColArr then begin
|
|
// Procesar Nueva Columna
|
|
Item:=GetDefaultColRow;
|
|
end else
|
|
if Sender=FRowArr then begin
|
|
// Procesar Nuevo Renglon
|
|
Item:=GetDefaultColRow;
|
|
end else begin
|
|
// Procesar Nueva Celda
|
|
Item:=nil;
|
|
end;
|
|
end;
|
|
|
|
constructor TVirtualGrid.Create;
|
|
begin
|
|
Inherited Create;
|
|
{$Ifdef DbgGrid}DebugLn('TVirtualGrid.Create');{$Endif}
|
|
FCellArr:=TPointerPointerArray.Create;
|
|
FCellArr.OnDestroyItem:=@doDestroyItem;
|
|
FCellArr.OnNewItem:=@doNewItem;
|
|
FColArr:= TPointerPointerArray.Create;
|
|
FColArr.OnDestroyItem:=@doDestroyItem;
|
|
FColArr.OnNewItem:=@doNewItem;
|
|
FRowArr:=TPointerPointerArray.Create;
|
|
FRowArr.OnDestroyItem:=@doDestroyItem;
|
|
FRowArr.OnNewItem:=@doNewItem;
|
|
RowCount:=4;
|
|
ColCount:=4;
|
|
end;
|
|
|
|
destructor TVirtualGrid.Destroy;
|
|
begin
|
|
{$Ifdef DbgGrid}DebugLn('TVirtualGrid.Destroy');{$Endif}
|
|
Clear;
|
|
FreeThenNil(FRowArr);
|
|
FreeThenNil(FColArr);
|
|
FreeThenNil(FCellArr);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TVirtualGrid.DeleteColRow(IsColumn: Boolean; index: Integer);
|
|
begin
|
|
FCellArr.DeleteColRow(IsColumn, index);
|
|
if IsColumn then begin
|
|
FColArr.DeleteColRow(True, index);
|
|
Dec(FColCount);
|
|
end else begin
|
|
FRowArr.DeleteColRow(True, index);
|
|
Dec(fRowCount);
|
|
end;
|
|
end;
|
|
|
|
procedure TVirtualGrid.MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer);
|
|
begin
|
|
FCellArr.MoveColRow(IsColumn, FromIndex, ToIndex);
|
|
if IsColumn then FColArr.MoveColRow(True, FromIndex, ToIndex)
|
|
else FRowArr.MoveColRow(True, FromIndex, ToIndex);
|
|
end;
|
|
|
|
procedure TVirtualGrid.ExchangeColRow(IsColumn: Boolean; index, WithIndex: Integer);
|
|
begin
|
|
FCellArr.ExchangeColRow(IsColumn, index, WithIndex);
|
|
if IsColumn then FColArr.ExchangeColRow(true, index, WithIndex)
|
|
else FRowArr.ExchangeColRow(True, index, WithIndex);
|
|
end;
|
|
|
|
procedure TVirtualGrid.InsertColRow(IsColumn: Boolean; Index: Integer);
|
|
begin
|
|
if IsColumn then begin
|
|
ColCount := ColCount + 1;
|
|
MoveColRow(true, ColCount-1, Index);
|
|
end else begin
|
|
RowCount := RowCount + 1;
|
|
MoveColRow(false, RowCount-1, Index);
|
|
end;
|
|
end;
|
|
|
|
procedure TStringCellEditor.WndProc(var TheMessage: TLMessage);
|
|
begin
|
|
{$IfDef GridTraceMsg}
|
|
TransMsg('StrCellEditor: ', TheMessage);
|
|
{$Endif}
|
|
if FGrid<>nil then
|
|
case TheMessage.Msg of
|
|
LM_CLEAR,
|
|
LM_CUT,
|
|
LM_PASTE:
|
|
begin
|
|
if FGrid.EditorIsReadOnly then
|
|
exit;
|
|
end;
|
|
end;
|
|
inherited WndProc(TheMessage);
|
|
end;
|
|
|
|
{ TStringCellEditor }
|
|
|
|
procedure TStringCellEditor.Change;
|
|
begin
|
|
{$IfDef DbgGrid} DebugLn('TStringCellEditor.Change INIT text=',Text);{$ENDIF}
|
|
inherited Change;
|
|
if (FGrid<>nil) and Visible then begin
|
|
FGrid.EditorTextChanged(FCol, FRow, Text);
|
|
end;
|
|
{$IfDef DbgGrid} DebugLn('TStringCellEditor.Change END');{$ENDIF}
|
|
end;
|
|
|
|
procedure TStringCellEditor.EditingDone;
|
|
begin
|
|
inherited EditingDone;
|
|
if FGrid<>nil then
|
|
FGrid.EditingDone;
|
|
end;
|
|
|
|
procedure TStringCellEditor.KeyDown(var Key: Word; Shift: TShiftState);
|
|
function AllSelected: boolean;
|
|
begin
|
|
result := (SelLength>0) and (SelLength=UTF8Length(Text));
|
|
end;
|
|
function AtStart: Boolean;
|
|
begin
|
|
Result:= (SelStart=0);
|
|
end;
|
|
function AtEnd: Boolean;
|
|
begin
|
|
result := ((SelStart+1)>UTF8Length(Text)) or AllSelected;
|
|
end;
|
|
procedure doEditorKeyDown;
|
|
begin
|
|
if FGrid<>nil then
|
|
FGrid.EditorkeyDown(Self, key, shift);
|
|
end;
|
|
procedure doGridKeyDown;
|
|
begin
|
|
if FGrid<>nil then
|
|
FGrid.KeyDown(Key, shift);
|
|
end;
|
|
function GetFastEntry: boolean;
|
|
begin
|
|
if FGrid<>nil then
|
|
Result := FGrid.FastEditing
|
|
else
|
|
Result := False;
|
|
end;
|
|
procedure CheckEditingKey;
|
|
begin
|
|
if (FGrid=nil) or FGrid.EditorIsReadOnly then
|
|
Key := 0;
|
|
end;
|
|
var
|
|
IntSel: boolean;
|
|
begin
|
|
{$IfDef dbgGrid}
|
|
DebugLn('TStringCellEditor.KeyDown INIT: Key=', Dbgs(Key),
|
|
' SelStart=',Dbgs(SelStart),' SelLenght=',dbgs(SelLength),
|
|
' Len(text)=',dbgs(Length(Text)),' Utf8Len(Text)=',dbgs(UTF8Length(Text)));
|
|
{$Endif}
|
|
inherited KeyDown(Key,Shift);
|
|
case Key of
|
|
VK_F2:
|
|
begin
|
|
doEditorKeyDown;
|
|
if (key<>0) then begin
|
|
if AllSelected then begin
|
|
SelLength := 0;
|
|
SelStart := Length(Text);
|
|
end else if GetFastEntry then
|
|
SelectAll;
|
|
end;
|
|
end;
|
|
VK_DELETE, VK_BACK:
|
|
begin
|
|
CheckEditingKey;
|
|
if key<>0 then
|
|
doEditorKeyDown;
|
|
end;
|
|
VK_UP, VK_DOWN:
|
|
doGridKeyDown;
|
|
VK_LEFT, VK_RIGHT:
|
|
begin
|
|
if GetFastEntry then begin
|
|
IntSel:=
|
|
((Key=VK_LEFT) and not AtStart) or
|
|
((Key=VK_RIGHT) and not AtEnd);
|
|
if not IntSel then
|
|
doGridKeyDown
|
|
else
|
|
doEditorKeyDown;
|
|
end else
|
|
doEditorKeyDown;
|
|
end;
|
|
VK_ESCAPE:
|
|
begin
|
|
doGridKeyDown;
|
|
if key<>0 then begin
|
|
SetEditText(FGrid.FEditorOldValue);
|
|
FGrid.EditorHide;
|
|
end;
|
|
end;
|
|
else
|
|
doEditorKeyDown;
|
|
end;
|
|
{$IfDef dbgGrid}
|
|
DebugLn('TStringCellEditor.KeyDown END: Key=', Dbgs(Key),
|
|
' SelStart=',Dbgs(SelStart),' SelLenght=',Dbgs(SelLength));
|
|
{$Endif}
|
|
end;
|
|
|
|
procedure TStringCellEditor.msg_SetMask(var Msg: TGridMessage);
|
|
begin
|
|
EditMask:=msg.Value;
|
|
end;
|
|
|
|
|
|
procedure TStringCellEditor.msg_SetValue(var Msg: TGridMessage);
|
|
begin
|
|
Text:=Msg.Value;
|
|
SelStart := UTF8Length(Text);
|
|
end;
|
|
|
|
procedure TStringCellEditor.msg_GetValue(var Msg: TGridMessage);
|
|
begin
|
|
Msg.Col:=FCol;
|
|
Msg.Row:=FRow;
|
|
Msg.Value:=Text;
|
|
end;
|
|
|
|
procedure TStringCellEditor.msg_SetGrid(var Msg: TGridMessage);
|
|
begin
|
|
FGrid:=Msg.Grid;
|
|
Msg.Options:=EO_AUTOSIZE or EO_SELECTALL or EO_HOOKKEYPRESS or EO_HOOKKEYUP;
|
|
end;
|
|
|
|
procedure TStringCellEditor.msg_SelectAll(var Msg: TGridMessage);
|
|
begin
|
|
SelectAll;
|
|
end;
|
|
|
|
procedure TStringCellEditor.msg_SetPos(var Msg: TGridMessage);
|
|
begin
|
|
FCol := Msg.Col;
|
|
FRow := Msg.Row;
|
|
end;
|
|
|
|
procedure TStringCellEditor.msg_GetGrid(var Msg: TGridMessage);
|
|
begin
|
|
Msg.Grid := FGrid;
|
|
Msg.Options:= EO_IMPLEMENTED;
|
|
end;
|
|
|
|
constructor TStringCellEditor.Create(Aowner: TComponent);
|
|
begin
|
|
inherited Create(Aowner);
|
|
AutoSize := false;
|
|
end;
|
|
|
|
{ TStringGridStrings }
|
|
|
|
function TStringGridStrings.ConvertIndexLineCol(Index: Integer; var Line, Col: Integer): boolean;
|
|
begin
|
|
if FIsCol then
|
|
if (Index < 0) or (Index >= FGrid.RowCount) then
|
|
Result := False
|
|
else begin
|
|
Line := FIndex;
|
|
Col := Index;
|
|
Result := True;
|
|
end
|
|
else
|
|
if (Index < 0) or (Index >= FGrid.ColCount) then
|
|
Result := False
|
|
else begin
|
|
Line := Index;
|
|
Col := FIndex;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TStringGridStrings.Clear;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if FIsCol then begin
|
|
for I := 0 to FGrid.RowCount - 1 do begin
|
|
FGrid.Cells[FIndex, I] := '';
|
|
FGrid.Objects[FIndex, I] := nil;
|
|
end;
|
|
end else begin
|
|
for I := 0 to FGrid.ColCount - 1 do begin
|
|
FGrid.Cells[I, FIndex] := '';
|
|
FGrid.Objects[I, FIndex] := nil;
|
|
end;
|
|
end;
|
|
FAddedCount := 0;
|
|
end;
|
|
|
|
function TStringGridStrings.Add(const S: string): Integer;
|
|
var
|
|
Line, Col: Integer;
|
|
begin
|
|
if ConvertIndexLineCol(FAddedCount, Line, Col) then begin
|
|
FGrid.Cells[Line, Col] := S;
|
|
Result := FAddedCount;
|
|
Inc(FAddedCount);
|
|
end else
|
|
Result := -1;
|
|
end;
|
|
|
|
function TStringGridStrings.Get(Index: Integer): string;
|
|
var
|
|
Line, Col: Integer;
|
|
begin
|
|
if ConvertIndexLineCol(Index, Line, Col) then
|
|
Result := FGrid.Cells[Line, Col]
|
|
else
|
|
Result := ''
|
|
end;
|
|
|
|
function TStringGridStrings.GetCount: Integer;
|
|
begin
|
|
if FIsCol then
|
|
Result := FGrid.RowCount
|
|
else
|
|
Result := FGrid.ColCount;
|
|
end;
|
|
|
|
function TStringGridStrings.GetObject(Index: Integer): TObject;
|
|
var
|
|
Line, Col: Integer;
|
|
begin
|
|
if ConvertIndexLineCol(Index, Line, Col) then
|
|
Result := FGrid.Objects[Line, Col]
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure TStringGridStrings.Put(Index: Integer; const S: string);
|
|
var
|
|
Line, Col: Integer;
|
|
|
|
procedure RaiseError;
|
|
begin
|
|
raise EGridException.Create('Can not add String');
|
|
end;
|
|
|
|
begin
|
|
if ConvertIndexLineCol(Index, Line, Col) then
|
|
FGrid.Cells[Line, Col] := S
|
|
else
|
|
RaiseError;
|
|
end;
|
|
|
|
procedure TStringGridStrings.PutObject(Index: Integer; aObject: TObject);
|
|
var
|
|
Line, Col: Integer;
|
|
|
|
procedure RaiseError;
|
|
begin
|
|
raise EGridException.Create('Can not add Object');
|
|
end;
|
|
|
|
begin
|
|
if ConvertIndexLineCol(Index, Line, Col) then
|
|
FGrid.Objects[Line, Col] := aObject
|
|
else
|
|
RaiseError;
|
|
end;
|
|
|
|
constructor TStringGridStrings.Create(aGrid: TCustomStringGrid; OwnerMap: TMap; aIscol: boolean;
|
|
aIndex: Longint);
|
|
begin
|
|
inherited Create;
|
|
FGrid := aGrid;
|
|
FIsCol := aIsCol;
|
|
FIndex := aIndex;
|
|
FOwner := OwnerMap;
|
|
if FOwner<>nil then
|
|
FOwner.Add(FIndex, Self);
|
|
end;
|
|
|
|
destructor TStringGridStrings.Destroy;
|
|
begin
|
|
if FOwner<>nil then
|
|
FOwner.Delete(FIndex);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TStringGridStrings.Assign(Source: TPersistent);
|
|
var
|
|
I, StrNum: Integer;
|
|
begin
|
|
if Source is TStrings then begin
|
|
try
|
|
BeginUpdate;
|
|
StrNum := TStrings(Source).Count;
|
|
if StrNum > GetCount then StrNum := GetCount;
|
|
for I := 0 to StrNum - 1 do begin
|
|
Put(I, TStrings(Source).Strings[I]);
|
|
PutObject(I, TStrings(Source).Objects[I]);
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure TStringGridStrings.Delete(Index: Integer);
|
|
begin
|
|
raise EGridException.Create('Can not delete value.');
|
|
end;
|
|
|
|
procedure TStringGridStrings.Insert(Index: Integer; const S: string);
|
|
begin
|
|
raise EGridException.Create('Can not insert value.');
|
|
end;
|
|
|
|
|
|
|
|
{ TCustomDrawGrid }
|
|
|
|
function TCustomDrawGrid.CellNeedsCheckboxBitmaps(const aCol, aRow: Integer): boolean;
|
|
var
|
|
C: TGridColumn;
|
|
begin
|
|
Result := false;
|
|
if (aRow>=FixedRows) and Columns.Enabled then begin
|
|
C := ColumnFromGridColumn(aCol);
|
|
result := (C<>nil) and (C.ButtonStyle=cbsCheckboxColumn)
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.DrawCellCheckboxBitmaps(const aCol, aRow: Integer;
|
|
const aRect: TRect);
|
|
var
|
|
AState: TCheckboxState;
|
|
begin
|
|
AState := cbUnchecked;
|
|
GetCheckBoxState(aCol, aRow, aState);
|
|
DrawGridCheckboxBitmaps(aCol, aRow, aRect, aState);
|
|
end;
|
|
|
|
function TCustomDrawGrid.GetEditorValue(ACol, ARow: Integer): String;
|
|
var
|
|
msg: TGridMessage;
|
|
begin
|
|
if Assigned(Editor) and Editor.Visible then begin
|
|
Msg.LclMsg.msg:=GM_GETVALUE;
|
|
Msg.grid:=Self;
|
|
Msg.Col:=ACol;
|
|
Msg.Row:=ARow;
|
|
Msg.Value:='';
|
|
Editor.Dispatch(Msg);
|
|
Result:=Msg.Value;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.CellClick(const ACol, ARow: Integer; const Button:TMouseButton);
|
|
begin
|
|
if (Button=mbLeft) and CellNeedsCheckboxBitmaps(ACol, ARow) then
|
|
ToggleCheckbox;
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.DrawCell(aCol,aRow: Integer; aRect: TRect;
|
|
aState:TGridDrawState);
|
|
var
|
|
OldDefaultDrawing: boolean;
|
|
begin
|
|
if Assigned(OnDrawCell) and not(CsDesigning in ComponentState) then begin
|
|
PrepareCanvas(aCol, aRow, aState);
|
|
if DefaultDrawing then
|
|
DefaultDrawCell(aCol, aRow, aRect, aState);
|
|
OnDrawCell(Self,aCol,aRow,aRect,aState)
|
|
end else begin
|
|
OldDefaultDrawing:=FDefaultDrawing;
|
|
FDefaultDrawing:=True;
|
|
try
|
|
PrepareCanvas(aCol, aRow, aState);
|
|
finally
|
|
FDefaultDrawing:=OldDefaultDrawing;
|
|
end;
|
|
DefaultDrawCell(aCol,aRow,aRect,aState);
|
|
end;
|
|
DrawCellGrid(aCol,aRow,aRect,aState);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.DrawFocusRect(aCol, aRow: Integer; ARect: TRect);
|
|
var
|
|
OldFocusColor: TColor;
|
|
OldPenMode: TFPPenMode;
|
|
DrawBits: Byte;
|
|
begin
|
|
// Draw focused cell if we have the focus
|
|
if DefaultDrawing and (Self.Focused or
|
|
(EditorAlwaysShown and ((Feditor=nil) or not Feditor.Focused))) then
|
|
begin
|
|
CalcFocusRect(aRect);
|
|
if FUseXORFeatures then begin
|
|
Canvas.SaveHandleState;
|
|
OldFocusColor := FFocusColor;
|
|
FFocusColor:= clWhite;
|
|
OldPenMode:=Canvas.Pen.Mode;
|
|
Canvas.Pen.Mode := pmXOR;
|
|
end;
|
|
DrawBits := BF_RECT;
|
|
if (goRowSelect in Options) then begin
|
|
if ((fTopLeft.x>FixedCols) or (FGCache.TLColOff<>0)) then
|
|
DrawBits := DrawBits and not BF_LEFT;
|
|
if (FGCache.VisibleGrid.Right<ColCount-1) then
|
|
DrawBits := DrawBits and not BF_RIGHT;
|
|
end;
|
|
DrawRubberRect(Canvas, aRect, FFocusColor, DrawBits);
|
|
if FUseXORFeatures then begin
|
|
Canvas.Pen.Mode := OldPenMode;
|
|
Canvas.RestoreHandleState;
|
|
FFocusColor := OldFocusColor;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomDrawGrid.GetCells(ACol, ARow: Integer): string;
|
|
begin
|
|
Result:=inherited GetCells(ACol, ARow);
|
|
if (ACol = FEditorCol) and (ARow = FEditorRow) then
|
|
Result:=GetEditorValue(ACol, ARow);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.GetCheckBoxState(const aCol, aRow: Integer;
|
|
var aState: TCheckboxState);
|
|
begin
|
|
if assigned(FOnGetCheckboxState) then
|
|
OnGetCheckboxState(self, aCol, aRow, aState);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.ColRowExchanged(IsColumn:Boolean; index, WithIndex: Integer);
|
|
begin
|
|
if not IsColumn or not Columns.Enabled then
|
|
Fgrid.ExchangeColRow(IsColumn, index, WithIndex);
|
|
if Assigned(OnColRowExchanged) then
|
|
OnColRowExchanged(Self, IsColumn, index, WithIndex);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.ColRowInserted(IsColumn: boolean; index: integer);
|
|
begin
|
|
if not IsColumn or not Columns.Enabled then
|
|
FGrid.InsertColRow(IsColumn, Index);
|
|
NotifyColRowChange(True, IsColumn, Index, Index);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.ColRowDeleted(IsColumn: Boolean; index: Integer);
|
|
begin
|
|
FGrid.DeleteColRow(IsColumn, index);
|
|
NotifyColRowChange(False, IsColumn, Index, Index);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.ColRowMoved(IsColumn: Boolean; FromIndex, ToIndex: Integer);
|
|
begin
|
|
inherited ColRowMoved(IsColumn, FromIndex, ToIndex);
|
|
|
|
// now move content, if Columns.Enabled and IsColumn then
|
|
// first row header has been already moved, what is in
|
|
// cells[0,0]-cells[colCount-1,0] doesn't matter because
|
|
// columns should take precedence.
|
|
FGrid.MoveColRow(IsColumn, FromIndex, ToIndex);
|
|
|
|
if Assigned(OnColRowMoved) then
|
|
OnColRowMoved(Self, IsColumn, FromIndex, toIndex);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.HeaderClick(IsColumn: Boolean; index: Integer);
|
|
begin
|
|
inherited HeaderClick(IsColumn, index);
|
|
if Assigned(OnHeaderClick) then OnHeaderClick(Self, IsColumn, index);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.HeaderSized(IsColumn: Boolean; index: Integer);
|
|
begin
|
|
inherited HeaderSized(IsColumn, index);
|
|
if Assigned(OnHeaderSized) then OnHeaderSized(Self, IsColumn, index);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.HeaderSizing(const IsColumn: boolean; const AIndex,
|
|
ASize: Integer);
|
|
begin
|
|
inherited HeaderSizing(IsColumn, AIndex, ASize);
|
|
if Assigned(OnHeaderSizing) then
|
|
OnHeaderSizing(self, IsColumn, AIndex, ASize);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.KeyDown(var Key: Word; Shift: TShiftState);
|
|
begin
|
|
inherited KeyDown(Key, Shift);
|
|
|
|
if (Key=VK_SPACE) and CellNeedsCheckboxBitmaps(col, row) then begin
|
|
ToggleCheckbox;
|
|
Key:=0;
|
|
end;
|
|
end;
|
|
|
|
function TCustomDrawGrid.GetEditMask(aCol, aRow: Longint): string;
|
|
begin
|
|
result:='';
|
|
if assigned(OnGetEditMask) then OnGetEditMask(self, aCol, aRow, Result);
|
|
end;
|
|
|
|
function TCustomDrawGrid.GetEditText(aCol, aRow: Longint): string;
|
|
begin
|
|
result:='';
|
|
if assigned(OnGetEditText) then OnGetEditText(self, aCol, aRow, Result);
|
|
FEditorOldValue:=Result;
|
|
FEditorCol:=aCol;
|
|
FEditorRow:=aRow;
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.GridMouseWheel(shift: TShiftState; Delta: Integer);
|
|
var
|
|
ScrollCols: boolean;
|
|
begin
|
|
if MouseWheelOption=mwCursor then
|
|
inherited GridMouseWheel(shift, Delta)
|
|
else
|
|
if Delta<>0 then begin
|
|
ScrollCols := (ssCtrl in shift);
|
|
if ScrollCols then
|
|
begin
|
|
if not TrySmoothScrollBy(Delta*DefaultColWidth, 0) then
|
|
TryScrollTo(FTopLeft.x+Delta, FTopLeft.y, True, False);
|
|
end else
|
|
begin
|
|
if not TrySmoothScrollBy(0, Delta*DefaultRowHeight*Mouse.WheelScrollLines) then
|
|
TryScrollTo(FTopLeft.x, FTopLeft.y+Delta, False, True); // scroll only 1 line if above scrolling failed (probably due to too high line)
|
|
end;
|
|
if EditorMode then
|
|
EditorPos;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.NotifyColRowChange(WasInsert, IsColumn: boolean;
|
|
FromIndex,ToIndex: Integer);
|
|
begin
|
|
if WasInsert then begin
|
|
if assigned(OnColRowInserted) then
|
|
OnColRowInserted(Self, IsColumn, FromIndex, ToIndex)
|
|
end else begin
|
|
if assigned(OnColRowDeleted) then
|
|
OnColRowDeleted(Self, IsColumn, FromIndex, ToIndex);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.SetEditText(ACol, ARow: Longint; const Value: string);
|
|
begin
|
|
if Assigned(OnSetEditText) then
|
|
OnSetEditText(Self, aCol, aRow, Value);
|
|
inherited SetEditText(aCol, aRow, Value);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.SizeChanged(OldColCount, OldRowCount: Integer);
|
|
begin
|
|
if OldColCount<>ColCount then begin
|
|
fGrid.ColCount:=ColCount;
|
|
if OldColCount>ColCount then
|
|
NotifyColRowChange(False, True, ColCount, OldColCount-1)
|
|
else
|
|
NotifyColRowChange(True, True, OldColCount, ColCount-1);
|
|
end;
|
|
if OldRowCount<>RowCount then begin
|
|
fGrid.RowCount:=RowCount;
|
|
if OldRowCount>RowCount then
|
|
NotifyColRowChange(False, False, RowCount, OldRowCount-1)
|
|
else
|
|
NotifyColRowChange(True, False, OldRowCount, RowCount-1);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.ToggleCheckbox;
|
|
var
|
|
TempColumn: TGridColumn;
|
|
AState: TCheckboxState;
|
|
begin
|
|
if not EditingAllowed(Col) then
|
|
exit;
|
|
|
|
TempColumn := ColumnFromGridColumn(Col);
|
|
if (TempColumn<>nil) and not TempColumn.ReadOnly then
|
|
begin
|
|
|
|
AState := cbGrayed;
|
|
GetCheckboxState(Col, Row, AState);
|
|
|
|
if AState=cbChecked then
|
|
AState := cbUnchecked
|
|
else
|
|
AState := cbChecked;
|
|
|
|
SetCheckboxState(Col, Row, AState);
|
|
|
|
if Assigned(OnCheckboxToggled) then
|
|
OnCheckboxToggled(self, Col, Row, AState);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.DrawCellAutonumbering(aCol, aRow: Integer;
|
|
aRect: TRect; const aValue: string);
|
|
begin
|
|
DrawCellText(aCol, aRow, aRect, [], aValue);
|
|
end;
|
|
|
|
function TCustomDrawGrid.SelectCell(aCol, aRow: Integer): boolean;
|
|
begin
|
|
Result := inherited SelectCell(aCol, aRow);
|
|
if Assigned(OnSelectCell) then
|
|
OnSelectCell(Self, aCol, aRow, Result);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.SetColor(Value: TColor);
|
|
begin
|
|
inherited SetColor(Value);
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.SetCheckboxState(const aCol, aRow: Integer;
|
|
const aState: TCheckboxState);
|
|
begin
|
|
if assigned(FOnSetCheckboxState) then begin
|
|
OnSetCheckboxState(self, aCol, aRow, aState);
|
|
if DefaultDrawing then
|
|
InvalidateCell(aCol, aRow);
|
|
end;
|
|
end;
|
|
|
|
function TCustomDrawGrid.CreateVirtualGrid: TVirtualGrid;
|
|
begin
|
|
Result:=TVirtualGrid.Create;
|
|
end;
|
|
|
|
constructor TCustomDrawGrid.Create(AOwner: TComponent);
|
|
begin
|
|
fGrid:=CreateVirtualGrid;
|
|
inherited Create(AOwner);
|
|
end;
|
|
|
|
destructor TCustomDrawGrid.Destroy;
|
|
begin
|
|
{$Ifdef DbgGrid}DebugLn('TCustomDrawGrid.Destroy');{$Endif}
|
|
FreeThenNil(FGrid);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.DeleteColRow(IsColumn: Boolean; index: Integer);
|
|
begin
|
|
DoOPDeleteColRow(IsColumn, Index);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.DeleteCol(Index: Integer);
|
|
begin
|
|
DeleteColRow(True, Index);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.DeleteRow(Index: Integer);
|
|
begin
|
|
DeleteColRow(False, Index);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.ExchangeColRow(IsColumn: Boolean; index,
|
|
WithIndex: Integer);
|
|
begin
|
|
DoOPExchangeColRow(IsColumn, Index, WithIndex);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.InsertColRow(IsColumn: boolean; index: integer);
|
|
begin
|
|
doOPInsertColRow(IsColumn, Index);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.MoveColRow(IsColumn: Boolean; FromIndex,
|
|
ToIndex: Integer);
|
|
begin
|
|
DoOPMoveColRow(IsColumn, FromIndex, ToIndex);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.SortColRow(IsColumn: Boolean; index: Integer);
|
|
begin
|
|
if IsColumn then begin
|
|
if (FFixedRows < RowCount) and (RowCount > 0) then
|
|
Sort(IsColumn, index, FFixedRows, RowCount-1)
|
|
end
|
|
else begin
|
|
if (FFixedCols < ColCount) and (ColCount > 0) then
|
|
Sort(IsColumn, index, FFixedCols, ColCount-1);
|
|
end
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.SortColRow(IsColumn: Boolean; Index, FromIndex,
|
|
ToIndex: Integer);
|
|
begin
|
|
Sort(IsColumn, Index, FromIndex, ToIndex);
|
|
end;
|
|
|
|
procedure TCustomDrawGrid.DefaultDrawCell(aCol, aRow: Integer; var aRect: TRect;
|
|
aState: TGridDrawState);
|
|
begin
|
|
if (FTitleStyle=tsNative) and (gdFixed in AState) then
|
|
DrawThemedCell(aCol, aRow, aRect, aState)
|
|
else
|
|
DrawFillRect(Canvas, aRect);
|
|
|
|
if CellNeedsCheckboxBitmaps(aCol,aRow) then
|
|
DrawCellCheckboxBitmaps(aCol,aRow,aRect)
|
|
else
|
|
begin
|
|
if IsCellButtonColumn(Point(aCol,aRow)) then begin
|
|
DrawButtonCell(aCol,aRow,aRect,aState);
|
|
end
|
|
else begin
|
|
if (goFixedRowNumbering in Options) and (ARow>=FixedRows) and (aCol=0) and
|
|
(FixedCols>0)
|
|
then
|
|
DrawCellAutonumbering(aCol, aRow, aRect, IntToStr(aRow-FixedRows+1));
|
|
end;
|
|
//draw text
|
|
if GetIsCellTitle(aCol, aRow) then
|
|
DrawColumnText(aCol, aRow, aRect, aState)
|
|
else
|
|
DrawTextInCell(aCol,aRow, aRect,aState);
|
|
end;
|
|
end;
|
|
|
|
{ TCustomStringGrid }
|
|
|
|
procedure TCustomStringGrid.MapFree(var aMap: TMap);
|
|
var
|
|
Iterator: TMapIterator;
|
|
SGL: TStringGridStrings;
|
|
begin
|
|
if AMap=nil then
|
|
exit;
|
|
Iterator := TMapIterator.Create(AMap);
|
|
Iterator.First;
|
|
while not Iterator.EOM do begin
|
|
Iterator.GetData(SGL);
|
|
if SGL<>nil then
|
|
SGL.Free;
|
|
Iterator.Next;
|
|
end;
|
|
Iterator.Free;
|
|
FreeAndNil(AMap);
|
|
end;
|
|
|
|
function TCustomStringGrid.MapGetColsRows(IsCols: boolean; Index: Integer;
|
|
var AMap: TMap): TStrings;
|
|
begin
|
|
if AMap=nil then
|
|
AMap := TMap.Create(itu4, SizeOf(TStringGridStrings));
|
|
|
|
if AMap.HasId(Index) then
|
|
AMap.GetData(index, Result)
|
|
else
|
|
Result:=TStringGridStrings.Create(Self, AMap, IsCols, index);
|
|
end;
|
|
|
|
function TCustomStringGrid.GetCells(ACol, ARow: Integer): string;
|
|
var
|
|
C: PCellProps;
|
|
begin
|
|
Result:='';
|
|
C:=FGrid.Celda[aCol,aRow];
|
|
if C<>nil then Result:=C^ .Text;
|
|
end;
|
|
|
|
function TCustomStringGrid.GetCols(index: Integer): TStrings;
|
|
begin
|
|
Result := MapGetColsRows(True, Index, FColsMap);
|
|
end;
|
|
|
|
function TCustomStringGrid.GetObjects(ACol, ARow: Integer): TObject;
|
|
var
|
|
C: PCellProps;
|
|
begin
|
|
Result:=nil;
|
|
C:=Fgrid.Celda[aCol,aRow];
|
|
if C<>nil then Result:=C^.Data;
|
|
end;
|
|
|
|
function TCustomStringGrid.GetRows(index: Integer): TStrings;
|
|
begin
|
|
Result := MapGetColsRows(False, Index, FRowsMap);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.ReadCells(Reader: TReader);
|
|
var
|
|
aCol,aRow: Integer;
|
|
i, c: Integer;
|
|
begin
|
|
with Reader do begin
|
|
ReadListBegin;
|
|
c := ReadInteger;
|
|
for i:=1 to c do begin
|
|
aCol := ReadInteger;
|
|
aRow := ReadInteger;
|
|
Cells[aCol,aRow]:= ReadString;
|
|
end;
|
|
{
|
|
repeat
|
|
aCol := ReadInteger;
|
|
aRow := ReadInteger;
|
|
Cells[aCol,aRow] := ReadString;
|
|
until NextValue = vaNull;
|
|
}
|
|
ReadListEnd;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.SetCells(ACol, ARow: Integer; const AValue: string);
|
|
procedure UpdateCell;
|
|
begin
|
|
if EditorMode and (aCol=FCol)and(aRow=FRow) and
|
|
not (gfEditorUpdateLock in GridFlags) then
|
|
begin
|
|
EditorDoSetValue;
|
|
end;
|
|
InvalidateCell(aCol, aRow);
|
|
end;
|
|
var
|
|
C: PCellProps;
|
|
begin
|
|
C:= FGrid.Celda[aCol,aRow];
|
|
if C<>nil then begin
|
|
if C^.Text<>nil then
|
|
StrDispose(C^.Text);
|
|
C^.Text:=StrNew(pchar(aValue));
|
|
UpdateCell;
|
|
FModified := True;
|
|
end else begin
|
|
if AValue<>'' then begin
|
|
New(C);
|
|
C^.Text:=StrNew(pchar(Avalue));
|
|
C^.Attr:=nil;
|
|
C^.Data:=nil;
|
|
FGrid.Celda[aCol,aRow]:=C;
|
|
UpdateCell;
|
|
FModified := True;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.SetCols(index: Integer; const AValue: TStrings);
|
|
var
|
|
SGL: TStringGridStrings;
|
|
begin
|
|
SGL := TStringGridStrings.Create(Self, nil, True, index);
|
|
SGL.Assign(AValue);
|
|
SGL.Free;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.SetObjects(ACol, ARow: Integer; AValue: TObject);
|
|
var
|
|
c: PCellProps;
|
|
begin
|
|
C:=FGrid.Celda[aCol,aRow];
|
|
if c<>nil then C^.Data:=AValue
|
|
else begin
|
|
c:=fGrid.GetDefaultCell;
|
|
c^.Data:=Avalue;
|
|
FGrid.Celda[aCol,aRow]:=c;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.SetRows(index: Integer; const AValue: TStrings);
|
|
var
|
|
SGL: TStringGridStrings;
|
|
begin
|
|
SGL := TStringGridStrings.Create(Self, nil, False, index);
|
|
SGL.Assign(AValue);
|
|
SGL.Free;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.WriteCells(Writer: TWriter);
|
|
var
|
|
i,j: Integer;
|
|
c: Integer;
|
|
begin
|
|
with writer do begin
|
|
WriteListBegin;
|
|
//cell count
|
|
c:=0;
|
|
for i:=0 to ColCount-1 do
|
|
for j:=0 to RowCount-1 do
|
|
if Cells[i,j]<>'' then Inc(c);
|
|
WriteInteger(c);
|
|
|
|
for i:=0 to ColCount-1 do
|
|
for j:=0 to RowCount-1 do
|
|
if Cells[i,j]<>'' then begin
|
|
WriteInteger(i);
|
|
WriteInteger(j);
|
|
WriteString(Cells[i,j]);
|
|
end;
|
|
WriteListEnd;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.CopyCellRectToClipboard(const R: TRect);
|
|
var
|
|
SelStr, SelHTMLStr: String;
|
|
aRow,aCol,k: LongInt;
|
|
|
|
function QuoteText(s: string): string;
|
|
begin
|
|
DoCellProcess(aCol, aRow, cpCopy, s);
|
|
if (pos(#9, s)>0) or
|
|
(pos(#10, s)>0) or
|
|
(pos(#13, s)>0)
|
|
then
|
|
result := AnsiQuotedStr(s, '"')
|
|
else
|
|
result := s;
|
|
end;
|
|
|
|
function PrepareToHTML(s: string): string;
|
|
var
|
|
i1: Integer;
|
|
s1: string;
|
|
begin
|
|
Result := '';
|
|
for i1 := 1 to Length(s) do
|
|
begin
|
|
case s[i1] of
|
|
#13: s1 := '<br>';
|
|
#10: if i1 > 1 then if s[i1 - 1] = #13 then s1 := '' else s1 := '<br>';
|
|
'<': s1 := '<';
|
|
'>': s1 := '>';
|
|
'"': s1 := '"';
|
|
'&': s1 := '&';
|
|
else s1 := s[i1];
|
|
end;
|
|
Result := Result + s1;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
SelStr := '';
|
|
|
|
SelHTMLStr := '<head><style><!--table br {mso-data-placement:same-cell;} --></style></head>' + #13#10 +
|
|
'<table>' + #13#10;
|
|
|
|
//<head>...</head> MS Excel crutch, otherwise Excel split merged cell if it found <br> tag
|
|
|
|
for aRow := R.Top to R.Bottom do begin
|
|
|
|
SelHTMLStr := SelHTMLStr + '<tr>' + #13#10;
|
|
|
|
for aCol := R.Left to R.Right do begin
|
|
|
|
if Columns.Enabled and (aCol >= FirstGridColumn) then begin
|
|
|
|
k := ColumnIndexFromGridColumn(aCol);
|
|
if not Columns[k].Visible then
|
|
continue;
|
|
|
|
if (aRow = 0) and (FixedRows > 0) then
|
|
begin
|
|
SelStr := SelStr + QuoteText(Columns[k].Title.Caption);
|
|
SelHTMLStr := SelHTMLStr + '<td>' + PrepareToHTML(Columns[k].Title.Caption) + '</td>' + #13#10;
|
|
end
|
|
else
|
|
begin
|
|
SelStr := SelStr + QuoteText(Cells[aCol,aRow]);
|
|
SelHTMLStr := SelHTMLStr + '<td>' + PrepareToHTML(Cells[aCol,aRow]) + '</td>' + #13#10;
|
|
end;
|
|
|
|
end else
|
|
begin
|
|
SelStr := SelStr + QuoteText(Cells[aCol,aRow]);
|
|
SelHTMLStr := SelHTMLStr + '<td>' + PrepareToHTML(Cells[aCol,aRow]) + '</td>' + #13#10;
|
|
end;
|
|
|
|
if aCol <> R.Right then
|
|
SelStr := SelStr + #9;
|
|
end;
|
|
|
|
SelStr := SelStr + sLineBreak;
|
|
SelHTMLStr := SelHTMLStr + '</tr>' + #13#10;
|
|
end;
|
|
SelHTMLStr := SelHTMLStr + #13#10 + '</table>';
|
|
Clipboard.SetAsHtml(SelHTMLStr, SelStr);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.AssignTo(Dest: TPersistent);
|
|
var
|
|
i, j: Integer;
|
|
begin
|
|
if Dest is TCustomStringGrid then begin
|
|
BeginUpdate;
|
|
inherited AssignTo(Dest);
|
|
for i:=0 to ColCount-1 do
|
|
for j:=0 to RowCount-1 do
|
|
TCustomStringGrid(Dest).Cells[i,j] := Cells[i,j];
|
|
EndUpdate;
|
|
end else
|
|
inherited AssignTo(Dest);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.AutoAdjustColumn(aCol: Integer);
|
|
var
|
|
i,W, imgWidth: Integer;
|
|
Ts: TSize;
|
|
TmpCanvas: TCanvas;
|
|
C: TGridColumn;
|
|
aRect: TRect;
|
|
isMultiLine, B: Boolean;
|
|
aText: string;
|
|
aLayout: TButtonLayout;
|
|
imgList: TCustomImageList;
|
|
begin
|
|
if not IsColumnIndexValid(aCol) then
|
|
Exit;
|
|
|
|
GetTitleImageInfo(aCol, i, aLayout);
|
|
if (i>=0) and (FTitleImageList<>nil) and (aLayout in [blGlyphLeft, blGlyphRight]) then
|
|
imgWidth := FTitleImageList.WidthForPPI[FTitleImageListWidth, Font.PixelsPerInch] + 2*DEFIMAGEPADDING
|
|
else
|
|
imgWidth := 0;
|
|
GetSortTitleImageInfo(aCol, imgList, i, W, B);
|
|
if (imgList<>nil) and (i>=0) then
|
|
Inc(imgWidth, imgList.WidthForPPI[W, Font.PixelsPerInch] + DEFIMAGEPADDING);
|
|
|
|
tmpCanvas := GetWorkingCanvas(Canvas);
|
|
|
|
C := ColumnFromGridColumn(aCol);
|
|
isMultiLine := (C<>nil) and C.Title.MultiLine;
|
|
|
|
try
|
|
W:=0;
|
|
for i := 0 to RowCount-1 do begin
|
|
|
|
if C<>nil then begin
|
|
if i<FixedRows then
|
|
tmpCanvas.Font := C.Title.Font
|
|
else
|
|
tmpCanvas.Font := C.Font;
|
|
end else begin
|
|
if i<FixedRows then
|
|
tmpCanvas.Font := TitleFont
|
|
else
|
|
tmpCanvas.Font := Font;
|
|
end;
|
|
|
|
if (i=0) and (FixedRows>0) and (C<>nil) then
|
|
aText := C.Title.Caption
|
|
else
|
|
aText := Cells[aCol, i];
|
|
|
|
if isMultiLine then begin
|
|
aRect := rect(0, 0, MaxInt, MaxInt);
|
|
DrawText(tmpCanvas.Handle, pchar(aText), Length(aText), aRect, DT_CALCRECT or DT_WORDBREAK);
|
|
Ts.cx := aRect.Right-aRect.Left;
|
|
end else
|
|
Ts := tmpCanvas.TextExtent(aText);
|
|
|
|
if Ts.Cx>W then
|
|
W := Ts.Cx;
|
|
end;
|
|
finally
|
|
if tmpCanvas<>Canvas then
|
|
FreeWorkingCanvas(tmpCanvas);
|
|
end;
|
|
|
|
W := W + imgWidth;
|
|
if W=0 then
|
|
W := DefaultColWidth
|
|
else
|
|
W := W + 2*varCellpadding + 1;
|
|
|
|
ColWidths[aCol] := W;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.CalcCellExtent(acol, aRow: Integer; var aRect: TRect);
|
|
var
|
|
S: string;
|
|
Ts: Tsize;
|
|
nc: PcellProps;
|
|
i: integer;
|
|
TextStyle : TTextStyle;
|
|
begin
|
|
inherited CalcCellExtent(acol,arow, aRect);
|
|
S:=Cells[aCol,aRow];
|
|
TextStyle := Canvas.TextStyle;
|
|
if not TextStyle.Clipping then begin
|
|
//if not FCellAttr.TextStyle.Clipping then begin
|
|
// Calcular el numero de celdas necesarias para contener todo
|
|
// El Texto
|
|
Ts:=Canvas.TextExtent(S);
|
|
i:=aCol;
|
|
while (Ts.Cx>(aRect.Right-aRect.Left))and(i<ColCount) do begin
|
|
inc(i);
|
|
Nc:=FGrid.Celda[i, aRow];
|
|
if (nc<>nil)and(Nc^.Text<>'')then Break;
|
|
aRect.Right:=aRect.Right + getColWidths(i);
|
|
end;
|
|
//fcellAttr.TextStyle.Clipping:=i<>aCol;
|
|
TextStyle.Clipping:=i<>aCol;
|
|
Canvas.TextStyle:=TextStyle;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.DefineProperties(Filer: TFiler);
|
|
begin
|
|
inherited DefineProperties(Filer);
|
|
DefineCellsProperty(Filer);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.DefineCellsProperty(Filer: TFiler);
|
|
function NeedCells: boolean;
|
|
var
|
|
i,j: integer;
|
|
AntGrid: TCustomStringGrid;
|
|
begin
|
|
result := false;
|
|
if Filer.Ancestor is TCustomStringGrid then begin
|
|
AntGrid := TCustomStringGrid(Filer.Ancestor);
|
|
result := (AntGrid.ColCount<>ColCount) or (AntGrid.RowCount<>RowCount);
|
|
if not result then
|
|
for i:=0 to AntGrid.ColCount-1 do
|
|
for j:=0 to AntGrid.RowCount-1 do
|
|
if Cells[i,j]<>AntGrid.Cells[i,j] then begin
|
|
result := true;
|
|
break;
|
|
end
|
|
end else
|
|
for i:=0 to ColCount-1 do
|
|
for j:=0 to RowCount-1 do
|
|
if Cells[i,j]<>'' then begin
|
|
result := true;
|
|
break;
|
|
end;
|
|
end;
|
|
begin
|
|
with Filer do begin
|
|
DefineProperty('Cells', @ReadCells, @WriteCells, NeedCells);
|
|
end;
|
|
end;
|
|
|
|
function TCustomStringGrid.DoCompareCells(Acol, ARow, Bcol, BRow: Integer): Integer;
|
|
begin
|
|
if Assigned(OnCompareCells) then
|
|
Result:=inherited DoCompareCells(Acol, ARow, Bcol, BRow)
|
|
else begin
|
|
Result:=UTF8CompareLatinTextFast(Cells[ACol,ARow], Cells[BCol,BRow]);
|
|
if SortOrder=soDescending then
|
|
result:=-result;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.DoCopyToClipboard;
|
|
begin
|
|
CopyCellRectToClipboard(Selection);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.DoCutToClipboard;
|
|
begin
|
|
if EditingAllowed(Col) then begin
|
|
doCopyToClipboard;
|
|
Clean(Selection, []);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.DoPasteFromClipboard;
|
|
begin
|
|
// Unpredictable results when a multiple selection is pasted back in.
|
|
// Therefore we inhibit this here.
|
|
if HasMultiSelection then
|
|
exit;
|
|
|
|
if EditingAllowed(Col) then
|
|
begin
|
|
if Clipboard.HasFormat(CF_TEXT) and not Clipboard.HasFormat(CF_HTML) then SelectionSetText(Clipboard.AsText);
|
|
if Clipboard.HasFormat(CF_TEXT) and Clipboard.HasFormat(CF_HTML) then SelectionSetHTML(Clipboard.GetAsHtml(True), Clipboard.AsText);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.DoCellProcess(aCol, aRow: Integer;
|
|
processType: TCellProcessType; var aValue: string);
|
|
begin
|
|
if Assigned(fOnCellProcess) then
|
|
OnCellProcess(Self, aCol, aRow, processType, aValue);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.DrawTextInCell(aCol, aRow: Integer; aRect: TRect;
|
|
aState: TGridDrawState);
|
|
begin
|
|
DrawCellText(aCol, aRow, aRect, aState, Cells[aCol,aRow]);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.DrawCellAutonumbering(aCol, aRow: Integer;
|
|
aRect: TRect; const aValue: string);
|
|
begin
|
|
if Cells[aCol,aRow]='' then
|
|
inherited DrawCellAutoNumbering(aCol,aRow,aRect,aValue);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.DrawColumnText(aCol, aRow: Integer; aRect: TRect;
|
|
aState: TGridDrawState);
|
|
begin
|
|
if Columns.Enabled then
|
|
inherited
|
|
else begin
|
|
DrawColumnTitleImage(aRect, aCol);
|
|
DrawCellText(aCol,aRow,aRect,aState,Cells[aCol,aRow])
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.GetCheckBoxState(const aCol, aRow: Integer;
|
|
var aState: TCheckboxState);
|
|
var
|
|
s:string;
|
|
begin
|
|
if Assigned(OnGetCheckboxState) then
|
|
inherited GetCheckBoxState(aCol, aRow, aState)
|
|
else begin
|
|
s := Cells[ACol, ARow];
|
|
if s=ColumnFromGridColumn(aCol).ValueChecked then
|
|
aState := cbChecked
|
|
else
|
|
if s=ColumnFromGridColumn(aCol).ValueUnChecked then
|
|
aState := cbUnChecked
|
|
else
|
|
aState := cbGrayed;
|
|
end;
|
|
end;
|
|
|
|
function TCustomStringGrid.GetEditText(aCol, aRow: Integer): string;
|
|
begin
|
|
Result:=Cells[aCol, aRow];
|
|
if Assigned(OnGetEditText) then OnGetEditText(Self, aCol, aRow, result);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.SaveContent(cfg: TXMLConfig);
|
|
var
|
|
i,j,k: Integer;
|
|
c: PCellProps;
|
|
begin
|
|
inherited SaveContent(cfg);
|
|
cfg.SetValue('grid/saveoptions/content', soContent in SaveOptions);
|
|
if soContent in SaveOptions then begin
|
|
// Save Cell Contents
|
|
k:=0;
|
|
For i:=0 to ColCount-1 do
|
|
For j:=0 to RowCount-1 do begin
|
|
C:=fGrid.Celda[i,j];
|
|
if (c<>nil) and (C^.Text<>'') then begin
|
|
Inc(k);
|
|
Cfg.SetValue('grid/content/cells/cellcount',k);
|
|
cfg.SetValue('grid/content/cells/cell'+IntToStr(k)+'/column',i);
|
|
cfg.SetValue('grid/content/cells/cell'+IntToStr(k)+'/row',j);
|
|
cfg.SetValue('grid/content/cells/cell'+IntToStr(k)+'/text', UTF8Decode(C^.Text));
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.SelectionSetText(TheText: String);
|
|
var
|
|
StartCol,StartRow: Integer;
|
|
Stream: TStringStream;
|
|
|
|
procedure LoadTSV(Fields: TStringList);
|
|
var
|
|
i, aCol, aRow: Integer;
|
|
NewValue: string;
|
|
begin
|
|
if StartRow<RowCount then begin
|
|
aRow := StartRow;
|
|
for i := 0 to Fields.Count-1 do begin
|
|
aCol := StartCol + i;
|
|
if (aCol<ColCount) and not GetColumnReadonly(aCol) then begin
|
|
NewValue := Fields[i];
|
|
if ValidateOnSetSelection and not ValidateEntry(aCol,aRow,Cells[aCol,aRow],NewValue) then
|
|
break;
|
|
DoCellProcess(aCol, aRow, cpPaste, NewValue);
|
|
Cells[aCol, aRow] := NewValue;
|
|
end;
|
|
end;
|
|
inc(StartRow);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Stream := TStringStream.Create(TheText);
|
|
try
|
|
StartCol := Selection.left;
|
|
StartRow := Selection.Top;
|
|
LCSVUtils.LoadFromCSVStream(Stream, @LoadTSV, #9);
|
|
finally
|
|
Stream.Free;
|
|
if ValidateOnSetSelection then
|
|
EditingDone;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TCustomStringGrid.SelectionSetHTML(TheHTML, TheText: String);
|
|
var
|
|
bStartCol, bStartRow, bCol, bRow: Integer;
|
|
bCellStr: string;
|
|
bSelRect: TRect;
|
|
|
|
bCellData, bTagEnd: Boolean;
|
|
bStr, bEndStr: PChar;
|
|
|
|
function ReplaceEntities(cSt: string): string;
|
|
var
|
|
o,a,b: pchar;
|
|
dName: widestring;
|
|
dEntity: WideChar;
|
|
begin
|
|
while true do begin
|
|
result := cSt;
|
|
if cSt = '' then
|
|
break;
|
|
o := @cSt[1];
|
|
a := strscan(o, '&');
|
|
if a = nil then
|
|
break;
|
|
b := strscan(a + 1, ';');
|
|
if b = nil then
|
|
break;
|
|
dName := UTF8Decode(copy(cSt, a - o + 2, b - a - 1));
|
|
dEntity := ' ';
|
|
if ResolveHTMLEntityReference(dName, dEntity) then begin
|
|
system.delete(cSt, a - o + 1, b - a + 1);
|
|
system.insert(UTF8Encode(dEntity), cSt, a - o + 1);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
if theHTML <> '' then
|
|
begin
|
|
bSelRect := Selection;
|
|
bStartCol := Selection.Left;
|
|
bStartRow := Selection.Top;
|
|
bCol := bStartCol;
|
|
bRow := bStartRow;
|
|
bStr := PChar(theHTML);
|
|
bEndStr := bStr + StrLen(bStr) - 4;
|
|
bCellStr := '';
|
|
bCellData := False;
|
|
|
|
while bStr < bEndStr do
|
|
begin
|
|
if bStr^ = #13 then // delete #13#10#20...#20 Excel place this after <br> tag.
|
|
begin
|
|
while bStr < (bEndStr - 1) do
|
|
begin
|
|
Inc(bStr);
|
|
if (bStr^ <> #10) and (bStr^ <> ' ') then Break;
|
|
end;
|
|
end;
|
|
if bStr^ = '<' then // tag start sign '<'
|
|
begin
|
|
bTagEnd := False;
|
|
Inc(bStr);
|
|
|
|
if UpCase(bStr^) = 'B' then
|
|
begin
|
|
Inc(bStr);
|
|
if (UpCase(bStr^) = 'R') and bCellData then bCellStr := bCellStr + #10; // tag <br> in table cell
|
|
end;
|
|
|
|
if bStr^ = '/' then // close tag sign '/'
|
|
begin
|
|
bTagEnd := True;
|
|
Inc(bStr);
|
|
end;
|
|
|
|
if UpCase(bStr^) = 'T' then
|
|
begin
|
|
Inc(bStr);
|
|
|
|
if UpCase(bStr^) = 'R' then // table start row tag <tr>
|
|
begin
|
|
bCellData := False;
|
|
if bTagEnd then // table end row tag </tr>
|
|
begin
|
|
bSelRect.Bottom := bRow;
|
|
Inc(bRow);
|
|
bCol := bStartCol;
|
|
end;
|
|
end;
|
|
|
|
if UpCase(bStr^) = 'D' then // table start cell tag <td>
|
|
begin
|
|
bCellData := not bTagEnd;
|
|
if bTagEnd then // table end cell tag </td>
|
|
begin
|
|
if IsColumnIndexValid(bCol) and IsRowIndexValid(bRow) then
|
|
begin
|
|
bCellStr := ReplaceEntities(bCellStr);
|
|
DoCellProcess(bCol, bRow, cpPaste, bCellStr);
|
|
Cells[bCol, bRow] := bCellStr;
|
|
end;
|
|
bSelRect.Right := bCol;
|
|
Inc(bCol);
|
|
bCellStr := '';
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
while bStr < bEndStr do
|
|
begin
|
|
Inc(bStr);
|
|
if bStr^ = '>' then // tag end sign '>'
|
|
begin
|
|
Inc(bStr);
|
|
Break;
|
|
end;
|
|
end;
|
|
end else
|
|
begin
|
|
if (bStr^ <> #13) and (bStr^ <> #10) and (bStr^ <> #9) and bCellData then bCellStr := bCellStr + bStr^;
|
|
Inc(bStr);
|
|
end;
|
|
end;
|
|
|
|
if (bCol = bStartCol) and (bRow = bStartRow) then
|
|
begin
|
|
DoCellProcess(bCol, bRow, cpPaste, TheText);
|
|
Cells[bCol, bRow] := TheText; //set text in cell if clipboard has CF_HTML fomat, but havent HTML table
|
|
end;
|
|
Selection := bSelRect; // set correct selection
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TCustomStringGrid.SetCheckboxState(const aCol, aRow: Integer;
|
|
const aState: TCheckboxState);
|
|
begin
|
|
if Assigned(OnSetCheckboxState) then
|
|
inherited SetCheckBoxState(aCol, aRow, aState)
|
|
else begin
|
|
if aState=cbChecked then
|
|
Cells[ACol, ARow] := ColumnFromGridColumn(aCol).ValueChecked
|
|
else
|
|
Cells[ACol, ARow] := ColumnFromGridColumn(aCol).ValueUnChecked;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.LoadContent(cfg: TXMLConfig; Version: Integer);
|
|
var
|
|
ContentSaved: Boolean;
|
|
i,j,k: Integer;
|
|
begin
|
|
inherited LoadContent(Cfg, Version);
|
|
if soContent in FSaveOptions then begin
|
|
ContentSaved:=Cfg.GetValue('grid/saveoptions/content', false);
|
|
if ContentSaved then begin
|
|
k:=cfg.getValue('grid/content/cells/cellcount', 0);
|
|
while k>0 do begin
|
|
i:=cfg.GetValue('grid/content/cells/cell'+IntToStr(k)+'/column', -1);
|
|
j:=cfg.GetValue('grid/content/cells/cell'+IntTostr(k)+'/row',-1);
|
|
if IsRowIndexValid(j) and IsColumnIndexValid(i) then
|
|
Cells[i,j]:=UTF8Encode(cfg.GetValue('grid/content/cells/cell'+IntToStr(k)+'/text',''));
|
|
Dec(k);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.Loaded;
|
|
begin
|
|
inherited Loaded;
|
|
FModified := False;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.SetEditText(aCol, aRow: Longint; const aValue: string);
|
|
begin
|
|
if not EditorIsReadOnly then begin
|
|
GridFlags := GridFlags + [gfEditorUpdateLock];
|
|
try
|
|
if Cells[aCol, aRow]<>aValue then
|
|
Cells[aCol, aRow]:= aValue;
|
|
finally
|
|
GridFlags := GridFlags - [gfEditorUpdateLock];
|
|
end;
|
|
end;
|
|
inherited SetEditText(aCol, aRow, aValue);
|
|
end;
|
|
|
|
constructor TCustomStringGrid.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
with DefaultTextStyle do begin
|
|
Alignment := taLeftJustify;
|
|
Layout := tlCenter;
|
|
Clipping := True;
|
|
//WordBreak := False
|
|
end;
|
|
ExtendedSelect := True;
|
|
SaveOptions := [soContent];
|
|
end;
|
|
|
|
destructor TCustomStringGrid.Destroy;
|
|
begin
|
|
MapFree(FRowsMap);
|
|
MapFree(FColsMap);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.AutoSizeColumn(aCol: Integer);
|
|
begin
|
|
AutoAdjustColumn(aCol);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.AutoSizeColumns;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to ColCount-1 do
|
|
AutoAdjustColumn(i)
|
|
end;
|
|
|
|
procedure TCustomStringGrid.Clean;
|
|
begin
|
|
Clean([gzNormal, gzFixedCols, gzFixedRows, gzFixedCells]);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.Clean(CleanOptions: TGridZoneSet);
|
|
begin
|
|
Clean(0,0,ColCount-1,RowCount-1, CleanOptions);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.Clean(aRect: TRect; CleanOptions: TGridZoneSet);
|
|
begin
|
|
with aRect do
|
|
Clean(Left, Top, Right, Bottom, CleanOptions);
|
|
end;
|
|
|
|
procedure TCustomStringGrid.Clean(StartCol, StartRow, EndCol, EndRow: integer;
|
|
CleanOptions: TGridZoneSet);
|
|
var
|
|
aCol: LongInt;
|
|
aRow: LongInt;
|
|
begin
|
|
if StartCol>EndCol then SwapInt(StartCol,EndCol);
|
|
if StartRow>EndRow then SwapInt(StartRow,EndRow);
|
|
|
|
if StartCol<0 then StartCol:=0;
|
|
if EndCol>ColCount-1 then EndCol:=ColCount-1;
|
|
if StartRow<0 then StartRow:=0;
|
|
if EndRow>RowCount-1 then EndRow:=RowCount-1;
|
|
|
|
BeginUpdate;
|
|
for aCol:=StartCol to EndCol do
|
|
for aRow:= StartRow to EndRow do
|
|
if (CleanOptions=[]) or (CellToGridZone(aCol,aRow) in CleanOptions) then
|
|
Cells[aCol,aRow] := '';
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.CopyToClipboard(AUseSelection: boolean = false);
|
|
begin
|
|
if AUseSelection then
|
|
doCopyToClipboard
|
|
else
|
|
CopyCellRectToClipboard(Rect(0,0,ColCount-1,RowCount-1));
|
|
end;
|
|
|
|
procedure TCustomStringGrid.InsertRowWithValues(Index: Integer;
|
|
Values: array of String);
|
|
var
|
|
i, OldRC, Diff: Integer;
|
|
begin
|
|
OldRC := RowCount;
|
|
Diff := Length(Values) - ColCount;
|
|
if Diff > 0 then
|
|
begin
|
|
if Columns.Enabled then
|
|
begin
|
|
for i := 1 to Diff do with Columns.Add do Title.Caption := '';
|
|
end
|
|
else
|
|
ColCount := Length(Values);
|
|
end;
|
|
InsertColRow(false, Index);
|
|
//if RowCount was 0, then setting ColCount restores RowCount (from FGridPropBackup)
|
|
//which is unwanted here, so reset it (Issue #0026943)
|
|
if (OldRc = 0) then RowCount := 1;
|
|
for i := 0 to Length(Values)-1 do
|
|
Cells[i, Index] := Values[i];
|
|
end;
|
|
|
|
procedure TCustomStringGrid.LoadFromCSVStream(AStream: TStream;
|
|
ADelimiter: Char=','; UseTitles: boolean=true; FromLine: Integer=0;
|
|
SkipEmptyLines: Boolean=true);
|
|
var
|
|
MaxCols: Integer = 0;
|
|
MaxRows: Integer = 0;
|
|
LineCounter: Integer = -1;
|
|
|
|
function RowOffset: Integer;
|
|
begin
|
|
// return row offset of current CSV record (MaxRows) which is 1 based
|
|
if UseTitles then
|
|
result := Max(0, FixedRows-1) + Max(MaxRows-1, 0)
|
|
else
|
|
result := FixedRows + Max(MaxRows-1, 0);
|
|
end;
|
|
|
|
procedure NewRecord(Fields:TStringlist);
|
|
var
|
|
i, aRow, aIndex: Integer;
|
|
begin
|
|
inc(LineCounter);
|
|
if (LineCounter < FromLine) then
|
|
exit;
|
|
|
|
if Fields.Count=0 then
|
|
exit;
|
|
|
|
if SkipEmptyLines and (Fields.Count=1) and (Fields[0]='') then
|
|
exit;
|
|
|
|
// make sure we have enough columns
|
|
if MaxCols<Fields.Count then
|
|
MaxCols := Fields.Count;
|
|
if Columns.Enabled then begin
|
|
while Columns.VisibleCount+FirstGridColumn>MaxCols do Columns.Delete(Columns.Count-1);
|
|
while Columns.VisibleCount+FirstGridColumn<MaxCols do Columns.Add;
|
|
end
|
|
else begin
|
|
if ColCount<MaxCols then
|
|
ColCount := MaxCols;
|
|
end;
|
|
|
|
// setup columns captions if enabled by UseTitles
|
|
if (MaxRows = 0) and UseTitles then begin
|
|
for i:= 0 to Fields.Count-1 do begin
|
|
if Columns.Enabled and (i>=FirstGridColumn) then begin
|
|
aIndex := ColumnIndexFromGridColumn(i);
|
|
if aIndex>=0 then
|
|
Columns[aIndex].Title.Caption:=Fields[i];
|
|
end else
|
|
Cells[i, 0] := Fields[i]
|
|
end;
|
|
inc(MaxRows);
|
|
exit;
|
|
end;
|
|
|
|
// Make sure we have enough rows
|
|
Inc(MaxRows);
|
|
aRow := RowOffset;
|
|
if aRow>RowCount-1 then
|
|
RowCount := aRow + 20;
|
|
|
|
// Copy line data to cells
|
|
for i:=0 to Fields.Count-1 do
|
|
Cells[i, aRow] := Fields[i];
|
|
end;
|
|
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
LCSVUtils.LoadFromCSVStream(AStream, @NewRecord, ADelimiter);
|
|
|
|
// last row offset + 1 (offset is 0 based)
|
|
RowCount := RowOffset + 1;
|
|
|
|
if not Columns.Enabled then
|
|
ColCount := MaxCols
|
|
else
|
|
while Columns.Count > MaxCols do
|
|
Columns.Delete(Columns.Count-1);
|
|
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.LoadFromCSVFile(AFilename: string;
|
|
ADelimiter: Char=','; UseTitles: boolean=true; FromLine: Integer=0;
|
|
SkipEmptyLines: Boolean=true);
|
|
var
|
|
TheStream: TFileStream;
|
|
begin
|
|
TheStream:=TFileStream.Create(AFileName,fmOpenRead or fmShareDenyWrite);
|
|
try
|
|
LoadFromCSVStream(TheStream, ADelimiter, UseTitles, FromLine, SkipEmptyLines);
|
|
finally
|
|
TheStream.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.SaveToCSVStream(AStream: TStream; ADelimiter: Char;
|
|
WriteTitles: boolean=true; VisibleColumnsOnly: boolean=false);
|
|
var
|
|
i,j,StartRow: Integer;
|
|
HeaderL, Lines: TStringList;
|
|
C: TGridColumn;
|
|
begin
|
|
if (RowCount=0) or (ColCount=0) then
|
|
exit;
|
|
Lines := TStringList.Create;
|
|
try
|
|
if WriteTitles then begin
|
|
if Columns.Enabled then begin
|
|
if FixedRows>0 then begin
|
|
HeaderL := TStringList.Create;
|
|
try
|
|
// Collect header column names to a temporary StringList
|
|
for i := 0 to ColCount-1 do begin
|
|
c := ColumnFromGridColumn(i);
|
|
if (c <> nil) then begin
|
|
if c.Visible or not VisibleColumnsOnly then
|
|
HeaderL.Add(c.Title.Caption);
|
|
end
|
|
else
|
|
if not VisibleColumnsOnly then
|
|
HeaderL.Add(Cells[i, 0]);
|
|
end;
|
|
HeaderL.Delimiter:=ADelimiter;
|
|
Headerl.StrictDelimiter := False; //force quoting of strings that contain whitespace or Delimiter
|
|
Lines.Add(HeaderL.DelimitedText); // Add as a first row in Lines
|
|
finally
|
|
HeaderL.Free;
|
|
end;
|
|
end;
|
|
StartRow := FixedRows;
|
|
end else
|
|
if FixedRows>0 then
|
|
StartRow := FixedRows-1
|
|
else
|
|
StartRow := 0;
|
|
end else
|
|
StartRow := FixedRows;
|
|
for i:=StartRow to RowCount-1 do begin
|
|
if Columns.Enabled and VisibleColumnsOnly then begin
|
|
HeaderL := TStringList.Create;
|
|
try
|
|
for j := 0 to ColCount-1 do begin
|
|
c := ColumnFromGridColumn(j);
|
|
if c=nil then Continue;
|
|
if c.Visible then
|
|
HeaderL.Add(Cells[j,i]);
|
|
end;
|
|
HeaderL.Delimiter:=ADelimiter;
|
|
HeaderL.StrictDelimiter := False; //force quoting of strings that contain whitespace or Delimiter
|
|
Lines.Add(HeaderL.DelimitedText); // Add the row in Lines
|
|
finally
|
|
HeaderL.Free;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Rows[i].StrictDelimiter := False; //force quoting of strings that contain whitespace or Delimiter
|
|
Rows[i].Delimiter:=ADelimiter;
|
|
Lines.Add(Rows[i].DelimitedText);
|
|
end;
|
|
end;
|
|
Lines.SaveToStream(AStream);
|
|
finally
|
|
Lines.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomStringGrid.SaveToCSVFile(AFileName: string; ADelimiter: Char;
|
|
WriteTitles: boolean=true; VisibleColumnsOnly: boolean=false);
|
|
var
|
|
TheStream: TFileStream;
|
|
begin
|
|
TheStream:=TFileStream.Create(AFileName,fmCreate);
|
|
try
|
|
SaveToCSVStream(TheStream, ADelimiter, WriteTitles, VisibleColumnsOnly);
|
|
finally
|
|
TheStream.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure Register;
|
|
begin
|
|
RegisterComponents('Additional',[TStringGrid,TDrawGrid]);
|
|
end;
|
|
|
|
|
|
{ TGridColumnTitle }
|
|
|
|
procedure TGridColumnTitle.WriteCaption(Writer: TWriter);
|
|
var
|
|
aStr: string;
|
|
PropInfo: PPropInfo;
|
|
begin
|
|
if not FIsDefaultCaption then aStr := FCaption
|
|
else aStr := Caption;
|
|
if Assigned(Writer.OnWriteStringProperty) then begin
|
|
PropInfo := GetPropInfo(Self, 'Caption');
|
|
Writer.OnWriteStringProperty(Writer, Self, PropInfo, aStr);
|
|
end;
|
|
Writer.WriteString(aStr);
|
|
end;
|
|
|
|
procedure TGridColumnTitle.FontChanged(Sender: TObject);
|
|
begin
|
|
FisDefaultTitleFont := False;
|
|
FColumn.ColumnChanged;
|
|
end;
|
|
|
|
function TGridColumnTitle.GetAlignment: TAlignment;
|
|
begin
|
|
if FAlignment = nil then
|
|
result := GetDefaultAlignment
|
|
else
|
|
result := FAlignment^;
|
|
end;
|
|
|
|
function TGridColumnTitle.GetCaption: TCaption;
|
|
begin
|
|
if (FCaption = nil) and FIsDefaultCaption then
|
|
result := GetDefaultCaption
|
|
else
|
|
result := FCaption;
|
|
end;
|
|
|
|
function TGridColumnTitle.GetColor: TColor;
|
|
begin
|
|
if FColor = nil then
|
|
result := GetDefaultColor
|
|
else
|
|
result := FColor^;
|
|
end;
|
|
|
|
procedure TGridColumnTitle.FillTitleDefaultFont;
|
|
var
|
|
AGrid: TCustomGrid;
|
|
begin
|
|
AGrid := FColumn.Grid;
|
|
if AGrid<>nil then
|
|
FFont.Assign( AGrid.TitleFont )
|
|
else
|
|
FFont.Assign( FColumn.Font );
|
|
FIsDefaultTitleFont := True;
|
|
end;
|
|
|
|
procedure TGridColumnTitle.FixDesignFontsPPI(const ADesignTimePPI: Integer);
|
|
var
|
|
LIsDefaultTitleFont: Boolean;
|
|
begin
|
|
LIsDefaultTitleFont := FIsDefaultTitleFont;
|
|
FColumn.Grid.DoFixDesignFontPPI(Font, ADesignTimePPI);
|
|
FIsDefaultTitleFont := LIsDefaultTitleFont;
|
|
end;
|
|
|
|
function TGridColumnTitle.GetFont: TFont;
|
|
begin
|
|
Result := FFont;
|
|
end;
|
|
|
|
function TGridColumnTitle.GetLayout: TTextLayout;
|
|
begin
|
|
if FLayout = nil then
|
|
result := GetDefaultLayout
|
|
else
|
|
result := FLayout^;
|
|
end;
|
|
|
|
function TGridColumnTitle.IsAlignmentStored: boolean;
|
|
begin
|
|
result := FAlignment <> nil;
|
|
end;
|
|
|
|
function TGridColumnTitle.IsCaptionStored: boolean;
|
|
begin
|
|
result := false;
|
|
end;
|
|
|
|
function TGridColumnTitle.IsColorStored: boolean;
|
|
begin
|
|
result := FColor <> nil;
|
|
end;
|
|
|
|
function TGridColumnTitle.IsFontStored: boolean;
|
|
begin
|
|
result := not IsDefaultFont;
|
|
end;
|
|
|
|
function TGridColumnTitle.IsLayoutStored: boolean;
|
|
begin
|
|
result := FLayout <> nil;
|
|
end;
|
|
|
|
procedure TGridColumnTitle.ScaleFontsPPI(const AToPPI: Integer; const AProportion: Double);
|
|
var
|
|
LIsDefaultTitleFont: Boolean;
|
|
begin
|
|
LIsDefaultTitleFont := FIsDefaultTitleFont;
|
|
FColumn.Grid.DoScaleFontPPI(Font, AToPPI, AProportion);
|
|
FIsDefaultTitleFont := LIsDefaultTitleFont;
|
|
end;
|
|
|
|
procedure TGridColumnTitle.SetAlignment(const AValue: TAlignment);
|
|
begin
|
|
if Falignment = nil then begin
|
|
if AValue = GetDefaultAlignment then
|
|
exit;
|
|
New(Falignment)
|
|
end else if FAlignment^ = AValue then
|
|
exit;
|
|
FAlignment^ := AValue;
|
|
FColumn.ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumnTitle.SetCaption(const AValue: TCaption);
|
|
begin
|
|
if (FCaption=nil)or(AValue<>StrPas(FCaption)) then begin
|
|
if FCaption<>nil then
|
|
StrDispose(FCaption);
|
|
FCaption := StrNew(PChar(AValue));
|
|
FIsDefaultCaption := false;
|
|
FColumn.ColumnChanged;
|
|
end;
|
|
end;
|
|
|
|
procedure TGridColumnTitle.DefineProperties(Filer: TFiler);
|
|
begin
|
|
inherited DefineProperties(Filer);
|
|
Filer.DefineProperty('Caption', nil, @WriteCaption, true);
|
|
end;
|
|
|
|
procedure TGridColumnTitle.SetColor(const AValue: TColor);
|
|
begin
|
|
if FColor=nil then begin
|
|
if AValue = GetDefaultColor then
|
|
exit;
|
|
New(FColor)
|
|
end else if FColor^=AValue then
|
|
exit;
|
|
FColor^ := AValue;
|
|
FColumn.ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumnTitle.SetFont(const AValue: TFont);
|
|
begin
|
|
if not FFont.IsEqual(AValue) then
|
|
FFont.Assign(AValue);
|
|
end;
|
|
|
|
procedure TGridColumnTitle.SetImageIndex(const AValue: TImageIndex);
|
|
begin
|
|
if FImageIndex = AValue then exit;
|
|
FImageIndex := AValue;
|
|
FColumn.ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumnTitle.SetImageLayout(const AValue: TButtonLayout);
|
|
begin
|
|
if FImageLayout = AValue then exit;
|
|
FImageLayout := AValue;
|
|
FColumn.ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumnTitle.SetLayout(const AValue: TTextLayout);
|
|
begin
|
|
if FLayout = nil then begin
|
|
if AValue = GetDefaultLayout then
|
|
exit;
|
|
New(FLayout)
|
|
end else if FLayout^ = AValue then
|
|
exit;
|
|
FLayout^ := AValue;
|
|
FColumn.ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumnTitle.SetMultiLine(const AValue: Boolean);
|
|
begin
|
|
if FMultiLine = AValue then exit;
|
|
FMultiLine := AValue;
|
|
FColumn.ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumnTitle.SetPrefixOption(const AValue: TPrefixOption);
|
|
begin
|
|
if FPrefixOption=AValue then exit;
|
|
FPrefixOption:=AValue;
|
|
FColumn.ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumnTitle.Assign(Source: TPersistent);
|
|
begin
|
|
if Source is TGridColumnTitle then begin
|
|
Alignment := TGridColumnTitle(Source).Alignment;
|
|
Layout := TGridColumnTitle(Source).Layout;
|
|
Caption := TGridColumnTitle(Source).Caption;
|
|
Color := TGridColumnTitle(Source).Color;
|
|
Font := TGridColumnTitle(Source).Font;
|
|
ImageIndex := TGridColumnTitle(Source).ImageIndex;
|
|
end else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
function TGridColumnTitle.GetDefaultCaption: string;
|
|
begin
|
|
Result := 'Title'
|
|
end;
|
|
|
|
function TGridColumnTitle.GetDefaultAlignment: TAlignment;
|
|
begin
|
|
result := taLeftJustify
|
|
end;
|
|
|
|
function TGridColumnTitle.GetDefaultColor: TColor;
|
|
begin
|
|
if FColumn.Grid <> nil then
|
|
result := FColumn.Grid.FixedColor
|
|
else
|
|
result := clBtnFace
|
|
end;
|
|
|
|
function TGridColumnTitle.GetDefaultLayout: TTextLayout;
|
|
begin
|
|
result := tlCenter
|
|
end;
|
|
|
|
function TGridColumnTitle.GetOwner: TPersistent;
|
|
begin
|
|
Result := FColumn;
|
|
end;
|
|
|
|
constructor TGridColumnTitle.Create(TheColumn: TGridColumn);
|
|
begin
|
|
inherited Create;
|
|
FColumn := TheColumn;
|
|
FIsDefaultTitleFont := True;
|
|
FFont := TFont.Create;
|
|
FillTitleDefaultFont;
|
|
FFont.OnChange := @FontChanged;
|
|
FImageIndex := -1;
|
|
FImageLayout := blGlyphRight;
|
|
FIsDefaultCaption := true;
|
|
end;
|
|
|
|
destructor TGridColumnTitle.Destroy;
|
|
begin
|
|
if FFont<>nil then FFont.Free;
|
|
if FAlignment<>nil then Dispose(FAlignment);
|
|
if FColor<>nil then Dispose(FColor);
|
|
if FCaption<>nil then StrDispose(FCaption); //DisposeStr(FCaption);
|
|
if FLayout<>nil then Dispose(FLayout);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TGridColumnTitle.IsDefault: boolean;
|
|
begin
|
|
Result := (FAlignment = nil) and (FColor = nil) and (FCaption = nil) and
|
|
IsDefaultFont and (FLayout = nil) and
|
|
(FImageIndex = 0) and (FImageLayout = blGlyphRight);
|
|
end;
|
|
|
|
{ TGridColumn }
|
|
|
|
procedure TGridColumn.FontChanged(Sender: TObject);
|
|
begin
|
|
FisDefaultFont := False;
|
|
ColumnChanged;
|
|
end;
|
|
|
|
function TGridColumn.GetAlignment: TAlignment;
|
|
begin
|
|
if FAlignment=nil then
|
|
Result := GetDefaultAlignment
|
|
else
|
|
Result := FAlignment^;
|
|
end;
|
|
|
|
function TGridColumn.GetColor: TColor;
|
|
begin
|
|
if FColor=nil then
|
|
result := GetDefaultColor
|
|
else
|
|
result := FColor^
|
|
end;
|
|
|
|
function TGridColumn.GetExpanded: Boolean;
|
|
begin
|
|
result := True;
|
|
end;
|
|
|
|
function TGridColumn.GetFont: TFont;
|
|
begin
|
|
result := FFont;
|
|
end;
|
|
|
|
function TGridColumn.GetGrid: TCustomGrid;
|
|
begin
|
|
if Collection is TGridColumns then
|
|
result := (Collection as TGridColumns).Grid
|
|
else
|
|
result := nil;
|
|
end;
|
|
|
|
function TGridColumn.GetLayout: TTextLayout;
|
|
begin
|
|
if FLayout=nil then
|
|
result := GetDefaultLayout
|
|
else
|
|
result := FLayout^;
|
|
end;
|
|
|
|
function TGridColumn.GetMaxSize: Integer;
|
|
begin
|
|
if FMaxSize=nil then
|
|
result := GetDefaultMaxSize
|
|
else
|
|
result := FMaxSize^;
|
|
end;
|
|
|
|
function TGridColumn.GetMinSize: Integer;
|
|
begin
|
|
if FMinSize=nil then
|
|
result := GetDefaultMinSize
|
|
else
|
|
result := FMinSize^;
|
|
end;
|
|
|
|
function TGridColumn.GetSizePriority: Integer;
|
|
begin
|
|
if not Visible then
|
|
result := 0
|
|
else
|
|
if FSizePriority=nil then
|
|
result := GetDefaultSizePriority
|
|
else
|
|
result := FSizePriority^;
|
|
end;
|
|
|
|
function TGridColumn.GetPickList: TStrings;
|
|
begin
|
|
Result := FPickList;
|
|
end;
|
|
|
|
function TGridColumn.GetReadOnly: Boolean;
|
|
begin
|
|
if FReadOnly=nil then
|
|
result := GetDefaultReadOnly
|
|
else
|
|
result := FReadOnly^;
|
|
end;
|
|
|
|
function TGridColumn.GetStoredWidth: Integer;
|
|
begin
|
|
if FWidth=nil then
|
|
result := -1
|
|
else
|
|
result := FWidth^;
|
|
end;
|
|
|
|
function TGridColumn.GetValueChecked: string;
|
|
begin
|
|
if FValueChecked = nil then
|
|
Result := GetDefaultValueChecked
|
|
else
|
|
Result := FValueChecked;
|
|
end;
|
|
|
|
function TGridColumn.GetValueUnchecked: string;
|
|
begin
|
|
if FValueUnChecked = nil then
|
|
Result := GetDefaultValueUnChecked
|
|
else
|
|
Result := FValueUnChecked;
|
|
end;
|
|
|
|
function TGridColumn.GetVisible: Boolean;
|
|
begin
|
|
if FVisible=nil then begin
|
|
result := GetDefaultVisible;
|
|
end else
|
|
result := FVisible^;
|
|
end;
|
|
|
|
function TGridColumn.GetWidth: Integer;
|
|
var
|
|
tmpGrid: TCustomGrid;
|
|
begin
|
|
{$ifdef newcols}
|
|
if not Visible then
|
|
exit(0);
|
|
{$endif}
|
|
if FWidth=nil then
|
|
result := GetDefaultWidth
|
|
else
|
|
result := FWidth^;
|
|
if (result<0) then
|
|
begin
|
|
tmpGrid := Grid;
|
|
if tmpGrid<>nil then
|
|
result := tmpGrid.DefaultColWidth;
|
|
end;
|
|
end;
|
|
|
|
function TGridColumn.IsAlignmentStored: boolean;
|
|
begin
|
|
result := FAlignment <> nil;
|
|
end;
|
|
|
|
function TGridColumn.IsColorStored: boolean;
|
|
begin
|
|
result := FColor <> nil;
|
|
end;
|
|
|
|
function TGridColumn.IsFontStored: boolean;
|
|
begin
|
|
result := not FisDefaultFont;
|
|
end;
|
|
|
|
function TGridColumn.IsLayoutStored: boolean;
|
|
begin
|
|
result := FLayout <> nil;
|
|
end;
|
|
|
|
function TGridColumn.IsMinSizeStored: boolean;
|
|
begin
|
|
result := FMinSize <> nil;
|
|
end;
|
|
|
|
function TGridColumn.IsMaxSizeStored: boolean;
|
|
begin
|
|
result := FMaxSize <> nil;
|
|
end;
|
|
|
|
function TGridColumn.IsReadOnlyStored: boolean;
|
|
begin
|
|
result := FReadOnly <> nil;
|
|
end;
|
|
|
|
function TGridColumn.IsSizePriorityStored: boolean;
|
|
begin
|
|
result := FSizePriority <> nil;
|
|
end;
|
|
|
|
function TGridColumn.IsValueCheckedStored: boolean;
|
|
begin
|
|
result := FValueChecked <> nil;
|
|
end;
|
|
|
|
function TGridColumn.IsValueUncheckedStored: boolean;
|
|
begin
|
|
Result := FValueUnchecked <> nil;
|
|
end;
|
|
|
|
function TGridColumn.IsVisibleStored: boolean;
|
|
begin
|
|
result := (FVisible<>nil) and not FVisible^;
|
|
end;
|
|
|
|
function TGridColumn.IsWidthStored: boolean;
|
|
begin
|
|
result := FWidth <> nil;
|
|
end;
|
|
|
|
procedure TGridColumn.ScaleFontsPPI(const AToPPI: Integer; const AProportion: Double);
|
|
var
|
|
LisDefaultFont: Boolean;
|
|
begin
|
|
LisDefaultFont := FisDefaultFont;
|
|
Grid.DoScaleFontPPI(Font, AToPPI, AProportion);
|
|
FisDefaultFont := LisDefaultFont;
|
|
Title.ScaleFontsPPI(AToPPI, AProportion);
|
|
end;
|
|
|
|
procedure TGridColumn.SetAlignment(const AValue: TAlignment);
|
|
begin
|
|
if FAlignment = nil then begin
|
|
if AValue=GetDefaultAlignment then
|
|
exit;
|
|
New(FAlignment);
|
|
end else if FAlignment^ = AValue then
|
|
exit;
|
|
FAlignment^ := AValue;
|
|
ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumn.SetButtonStyle(const AValue: TColumnButtonStyle);
|
|
begin
|
|
if FButtonStyle=AValue then exit;
|
|
FButtonStyle:=AValue;
|
|
ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumn.SetColor(const AValue: TColor);
|
|
begin
|
|
if FColor = nil then begin
|
|
if AValue=GetDefaultColor then
|
|
exit;
|
|
New(FColor)
|
|
end else if FColor^ = AValue then
|
|
exit;
|
|
FColor^ := AValue;
|
|
ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumn.SetExpanded(const AValue: Boolean);
|
|
begin
|
|
//todo
|
|
end;
|
|
|
|
procedure TGridColumn.SetFont(const AValue: TFont);
|
|
begin
|
|
if not FFont.IsEqual(AValue) then
|
|
FFont.Assign(AValue);
|
|
end;
|
|
|
|
procedure TGridColumn.SetLayout(const AValue: TTextLayout);
|
|
begin
|
|
if FLayout = nil then begin
|
|
if AValue=GetDefaultLayout then
|
|
exit;
|
|
New(FLayout)
|
|
end else if FLayout^ = AValue then
|
|
exit;
|
|
FLayout^ := AValue;
|
|
ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumn.SetMaxSize(const AValue: Integer);
|
|
begin
|
|
if FMaxSize = nil then begin
|
|
if AValue = GetDefaultMaxSize then
|
|
exit;
|
|
New(FMaxSize)
|
|
end else if FMaxSize^ = AVAlue then
|
|
exit;
|
|
FMaxSize^ := AValue;
|
|
ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumn.SetMinSize(const Avalue: Integer);
|
|
begin
|
|
if FMinSize = nil then begin
|
|
if AValue = GetDefaultMinSize then
|
|
exit;
|
|
New(FMinSize)
|
|
end else if FMinSize^ = AVAlue then
|
|
exit;
|
|
FMinSize^ := AValue;
|
|
ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumn.SetPickList(const AValue: TStrings);
|
|
begin
|
|
if AValue=nil then
|
|
FPickList.Clear
|
|
else
|
|
FPickList.Assign(AValue);
|
|
end;
|
|
|
|
procedure TGridColumn.SetReadOnly(const AValue: Boolean);
|
|
begin
|
|
if FReadOnly = nil then begin
|
|
if AValue = GetDefaultReadOnly then
|
|
exit;
|
|
New(FReadOnly)
|
|
end else if FReadOnly^ = AValue then
|
|
exit;
|
|
FReadOnly^ := Avalue;
|
|
ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumn.SetSizePriority(const AValue: Integer);
|
|
begin
|
|
if FSizePriority = nil then begin
|
|
if AValue = GetDefaultSizePriority then
|
|
exit;
|
|
New(FSizePriority)
|
|
end else if FSizePriority^ = AVAlue then
|
|
exit;
|
|
FSizePriority^ := AValue;
|
|
ColumnChanged;
|
|
end;
|
|
|
|
procedure TGridColumn.SetTitle(const AValue: TGridColumnTitle);
|
|
begin
|
|
FTitle.Assign(AValue);
|
|
end;
|
|
|
|
procedure TGridColumn.SetValueChecked(const AValue: string);
|
|
begin
|
|
if (FValueChecked=nil)or(CompareText(AValue, FValueChecked)<>0) then begin
|
|
if FValueChecked<>nil then
|
|
StrDispose(FValueChecked)
|
|
else
|
|
if CompareText(AValue, GetDefaultValueChecked)=0 then
|
|
exit;
|
|
FValueChecked := StrNew(PChar(AValue));
|
|
Changed(False);
|
|
end;
|
|
end;
|
|
|
|
procedure TGridColumn.SetValueUnchecked(const AValue: string);
|
|
begin
|
|
if (FValueUnchecked=nil)or(CompareText(AValue, FValueUnchecked)<>0) then begin
|
|
if FValueUnchecked<>nil then
|
|
StrDispose(FValueUnchecked)
|
|
else
|
|
if CompareText(AValue, GetDefaultValueUnchecked)=0 then
|
|
exit;
|
|
FValueUnchecked := StrNew(PChar(AValue));
|
|
Changed(False);
|
|
end;
|
|
end;
|
|
|
|
procedure TGridColumn.SetVisible(const AValue: Boolean);
|
|
begin
|
|
if FVisible = nil then begin
|
|
if AValue=GetDefaultVisible then
|
|
exit;
|
|
New(FVisible)
|
|
end else if FVisible^ = AValue then
|
|
exit;
|
|
FVisible^ := AValue;
|
|
AllColumnsChange;
|
|
end;
|
|
|
|
procedure TGridColumn.SetWidth(const AValue: Integer);
|
|
begin
|
|
if (AValue=0) and not Visible then
|
|
exit;
|
|
if AValue>=0 then begin
|
|
if FWidth = nil then begin
|
|
New(FWidth)
|
|
end else if FWidth^ = AVAlue then
|
|
exit;
|
|
FWidth^ := AValue;
|
|
end else begin
|
|
// negative value is handed over - dispose FWidth to use DefaultWidth
|
|
if FWidth <> nil then begin
|
|
Dispose(FWidth);
|
|
FWidth := nil;
|
|
end else
|
|
exit;
|
|
end;
|
|
FWidthChanged:=true;
|
|
ColumnChanged;
|
|
end;
|
|
|
|
function TGridColumn.GetDefaultReadOnly: boolean;
|
|
begin
|
|
result := false;
|
|
end;
|
|
|
|
function TGridColumn.GetDefaultLayout: TTextLayout;
|
|
begin
|
|
result := tlCenter
|
|
end;
|
|
|
|
function TGridColumn.GetDefaultVisible: boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
function TGridColumn.GetDefaultValueChecked: string;
|
|
begin
|
|
result := '1';
|
|
end;
|
|
|
|
function TGridColumn.GetDefaultValueUnchecked: string;
|
|
begin
|
|
result := '0';
|
|
end;
|
|
|
|
function TGridColumn.GetDefaultWidth: Integer;
|
|
var
|
|
tmpGrid: TCustomGrid;
|
|
begin
|
|
tmpGrid := Grid;
|
|
if tmpGrid<>nil then
|
|
result := tmpGrid.DefaultColWidth
|
|
else
|
|
result := -1;
|
|
end;
|
|
|
|
function TGridColumn.GetDefaultMaxSize: Integer;
|
|
begin
|
|
Result := DEFMAXSIZE;
|
|
end;
|
|
|
|
function TGridColumn.GetDefaultMinSize: Integer;
|
|
begin
|
|
result := DEFMINSIZE;
|
|
end;
|
|
|
|
function TGridColumn.GetDefaultColor: TColor;
|
|
var
|
|
TmpGrid: TCustomGrid;
|
|
begin
|
|
TmpGrid := Grid;
|
|
if TmpGrid<>nil then
|
|
result := TmpGrid.Color
|
|
else
|
|
result := clWindow
|
|
end;
|
|
|
|
function TGridColumn.GetDefaultSizePriority: Integer;
|
|
begin
|
|
Result := DEFSIZEPRIORITY;
|
|
end;
|
|
|
|
procedure TGridColumn.Assign(Source: TPersistent);
|
|
begin
|
|
if Source is TGridColumn then begin
|
|
//DebugLn('Assigning TGridColumn[',dbgs(Index),'] a TgridColumn')
|
|
Collection.BeginUpdate;
|
|
try
|
|
Alignment := TGridColumn(Source).Alignment;
|
|
ButtonStyle := TGridColumn(Source).ButtonStyle;
|
|
Color := TGridColumn(Source).Color;
|
|
DropDownRows := TGridColumn(Source).DropDownRows;
|
|
//Expanded := TGridColumn(Source).Expanded; //todo
|
|
Font := TGridColumn(Source).Font;
|
|
Layout := TGridColumn(Source).Layout;
|
|
MinSize := TGridColumn(Source).MinSize;
|
|
MaxSize := TGridColumn(Source).MaxSize;
|
|
PickList := TGridColumn(Source).PickList;
|
|
ReadOnly := TGridColumn(Source).ReadOnly;
|
|
SizePriority := TGridColumn(Source).SizePriority;
|
|
Title := TGridColumn(Source).Title;
|
|
Width := TGridCOlumn(Source).Width;
|
|
Visible := TGridColumn(Source).Visible;
|
|
finally
|
|
Collection.EndUpdate;
|
|
end;
|
|
end else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
function TGridColumn.GetDisplayName: string;
|
|
begin
|
|
if Title.Caption<>'' then
|
|
Result := Title.Caption
|
|
else
|
|
Result := 'GridColumn';
|
|
end;
|
|
|
|
function TGridColumn.GetDefaultAlignment: TAlignment;
|
|
begin
|
|
if ButtonStyle in [cbsCheckboxColumn,cbsButtonColumn] then
|
|
result := taCenter
|
|
else
|
|
result := taLeftJustify;
|
|
end;
|
|
|
|
procedure TGridColumn.ColumnChanged;
|
|
begin
|
|
Changed(False);
|
|
FWidthChanged := False;
|
|
end;
|
|
|
|
procedure TGridColumn.AllColumnsChange;
|
|
begin
|
|
Changed(True);
|
|
FWidthChanged := False;
|
|
end;
|
|
|
|
function TGridColumn.CreateTitle: TGridColumnTitle;
|
|
begin
|
|
result := TGridColumnTitle.Create(Self);
|
|
end;
|
|
|
|
procedure TGridColumn.SetIndex(Value: Integer);
|
|
var
|
|
AGrid: TCustomGrid;
|
|
CurCol,DstCol: Integer;
|
|
begin
|
|
AGrid := Grid;
|
|
if (Value<>Index) and (AGrid<>nil) then begin
|
|
// move grid content
|
|
CurCol := Grid.GridColumnFromColumnIndex(Index);
|
|
DstCol := Grid.GridColumnFromColumnIndex(Value);
|
|
if (CurCol>=0) and (DstCol>=0) then begin
|
|
AGrid.GridFlags:=AGrid.GridFlags + [gfColumnsLocked];
|
|
AGrid.DoOPMoveColRow(true, CurCol, DstCol);
|
|
AGrid.GridFlags:=AGrid.GridFlags - [gfColumnsLocked];
|
|
end;
|
|
end;
|
|
// move column item index
|
|
inherited SetIndex(Value);
|
|
end;
|
|
|
|
constructor TGridColumn.Create(ACollection: TCollection);
|
|
begin
|
|
inherited Create(ACollection);
|
|
FTitle := CreateTitle;
|
|
|
|
FIsDefaultFont := True;
|
|
FFont := TFont.Create;
|
|
FillDefaultFont;
|
|
FFont.OnChange := @FontChanged;
|
|
|
|
FPickList:= TStringList.Create;
|
|
FButtonStyle := cbsAuto;
|
|
FDropDownRows := 7;
|
|
end;
|
|
|
|
destructor TGridColumn.Destroy;
|
|
begin
|
|
if FAlignment<>nil then Dispose(FAlignment);
|
|
if FColor<>nil then Dispose(FColor);
|
|
if FVisible<>nil then Dispose(FVisible);
|
|
if FReadOnly<>nil then Dispose(FReadOnly);
|
|
if FWidth<>nil then Dispose(FWidth);
|
|
if FLayout<>nil then Dispose(FLayout);
|
|
if FMaxSize<>nil then Dispose(FMaxSize);
|
|
if FMinSize<>nil then Dispose(FMinSize);
|
|
if FSizePriority<>nil then Dispose(FSizePriority);
|
|
if FValueChecked<>nil then StrDispose(FValueChecked);
|
|
if FValueUnchecked<>nil then StrDispose(FValueUnchecked);
|
|
|
|
FreeThenNil(FPickList);
|
|
FreeThenNil(FFont);
|
|
FreeThenNil(FTitle);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TGridColumn.FillDefaultFont;
|
|
var
|
|
AGrid: TCustomGrid;
|
|
begin
|
|
AGrid := Grid;
|
|
if (AGrid<>nil) then begin
|
|
FFont.Assign(AGrid.Font);
|
|
FIsDefaultFont := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TGridColumn.FixDesignFontsPPI(const ADesignTimePPI: Integer);
|
|
var
|
|
LisDefaultFont: Boolean;
|
|
begin
|
|
LisDefaultFont := FisDefaultFont;
|
|
Grid.DoFixDesignFontPPI(Font, ADesignTimePPI);
|
|
FisDefaultFont := LisDefaultFont;
|
|
Title.FixDesignFontsPPI(ADesignTimePPI);
|
|
end;
|
|
|
|
function TGridColumn.IsDefault: boolean;
|
|
begin
|
|
result := FTitle.IsDefault and (FAlignment=nil) and (FColor=nil)
|
|
and (FVisible=nil) and (FReadOnly=nil) and (FWidth=nil) and FIsDefaultFont
|
|
and (FLayout=nil) and (FMaxSize=nil) and (FMinSize=nil)
|
|
and (FSizePriority=nil);
|
|
end;
|
|
|
|
{ TGridColumns }
|
|
|
|
function TGridColumns.GetColumn(Index: Integer): TGridColumn;
|
|
begin
|
|
result := TGridColumn( inherited Items[Index] );
|
|
end;
|
|
|
|
function TGridColumns.GetEnabled: Boolean;
|
|
begin
|
|
result := VisibleCount > 0;
|
|
end;
|
|
|
|
procedure TGridColumns.SetColumn(Index: Integer; Value: TGridColumn);
|
|
begin
|
|
Items[Index].Assign( Value );
|
|
end;
|
|
|
|
function TGridColumns.GetVisibleCount: Integer;
|
|
{$ifNdef newcols}
|
|
var
|
|
i: Integer;
|
|
{$endif}
|
|
begin
|
|
{$ifdef newcols}
|
|
result := Count;
|
|
{$else}
|
|
result := 0;
|
|
for i:=0 to Count-1 do
|
|
if Items[i].Visible then
|
|
inc(result);
|
|
{$endif}
|
|
end;
|
|
|
|
function TGridColumns.GetOwner: TPersistent;
|
|
begin
|
|
Result := FGrid;
|
|
end;
|
|
|
|
procedure TGridColumns.Update(Item: TCollectionItem);
|
|
begin
|
|
//if (FGrid<>nil) and not (csLoading in FGrid.ComponentState) then
|
|
FGrid.ColumnsChanged(TGridColumn(Item));
|
|
end;
|
|
|
|
procedure TGridColumns.TitleFontChanged;
|
|
var
|
|
c: TGridColumn;
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to Count-1 do begin
|
|
c := Items[i];
|
|
if (c<>nil)and(c.Title.IsDefaultFont) then begin
|
|
c.Title.FillTitleDefaultFont;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TGridColumns.FontChanged;
|
|
var
|
|
c: TGridColumn;
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to Count-1 do begin
|
|
c := Items[i];
|
|
if (c<>nil)and(c.IsDefaultFont) then begin
|
|
c.FillDefaultFont;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TGridColumns.RemoveColumn(Index: Integer);
|
|
begin
|
|
if HasIndex(Index) then
|
|
Delete(Index)
|
|
else
|
|
raise Exception.Create('Index out of range')
|
|
end;
|
|
|
|
procedure TGridColumns.MoveColumn(FromIndex, ToIndex: Integer);
|
|
begin
|
|
if HasIndex(FromIndex) then
|
|
if HasIndex(ToIndex) then
|
|
Items[FromIndex].Index := ToIndex
|
|
else
|
|
raise Exception.Create('ToIndex out of range')
|
|
else
|
|
raise Exception.Create('FromIndex out of range')
|
|
end;
|
|
|
|
procedure TGridColumns.ExchangeColumn(Index, WithIndex: Integer);
|
|
begin
|
|
if HasIndex(Index) then
|
|
if HasIndex(WithIndex) then begin
|
|
BeginUpdate;
|
|
if Index < WithIndex then
|
|
begin
|
|
Items[WithIndex].Index := Index;
|
|
Items[Index+1].Index := WithIndex;
|
|
end else
|
|
begin
|
|
Items[Index].Index := WithIndex;
|
|
Items[WithIndex+1].Index := Index;
|
|
end;
|
|
EndUpdate;
|
|
end else
|
|
raise Exception.Create('WithIndex out of range')
|
|
else
|
|
raise Exception.Create('Index out of range')
|
|
end;
|
|
|
|
procedure TGridColumns.InsertColumn(Index: Integer);
|
|
begin
|
|
FGrid.BeginUpdate;
|
|
Add;
|
|
MoveColumn(Count-1, Index);
|
|
FGrid.EndUpdate;
|
|
end;
|
|
|
|
constructor TGridColumns.Create(AGrid: TCustomGrid;
|
|
aItemClass: TCollectionItemClass);
|
|
begin
|
|
inherited Create( aItemClass );
|
|
FGrid := AGrid;
|
|
end;
|
|
|
|
function TGridColumns.Add: TGridColumn;
|
|
begin
|
|
result := TGridColumn( inherited add );
|
|
end;
|
|
|
|
procedure TGridColumns.Clear;
|
|
begin
|
|
BeginUpdate;
|
|
inherited Clear;
|
|
EndUpdate
|
|
end;
|
|
|
|
function TGridColumns.ColumnByTitle(const aTitle: string): TGridColumn;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
result := nil;
|
|
for i:=0 to Count-1 do
|
|
if SameText(Items[i].Title.Caption, aTitle) then begin
|
|
result := Items[i];
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function TGridColumns.RealIndex(Index: Integer): Integer;
|
|
{$ifNdef NewCols}
|
|
var
|
|
i: Integer;
|
|
{$endif}
|
|
begin
|
|
{$ifdef NewCols}
|
|
if Index>Count-1 then
|
|
result := -1
|
|
else
|
|
result := Index;
|
|
{$else}
|
|
result := -1;
|
|
if Index>=0 then
|
|
for i:=0 to Count-1 do begin
|
|
if Items[i].Visible then begin
|
|
Dec(index);
|
|
if Index<0 then begin
|
|
result := i;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
{$endif}
|
|
end;
|
|
|
|
function TGridColumns.IndexOf(Column: TGridColumn): Integer;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
result := -1;
|
|
for i:=0 to Count-1 do
|
|
if Items[i]=Column then begin
|
|
result := i;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function TGridColumns.IsDefault: boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
result := True;
|
|
for i:=0 to Count-1 do
|
|
result := Result and Items[i].IsDefault;
|
|
end;
|
|
|
|
function TGridColumns.HasIndex(Index: Integer): boolean;
|
|
begin
|
|
result := (index>-1)and(index<count);
|
|
end;
|
|
|
|
function TGridColumns.VisibleIndex(Index: Integer): Integer;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
result := -1;
|
|
if HasIndex(Index) and Items[Index].Visible then
|
|
for i:=0 to Index do
|
|
if Items[i].Visible then
|
|
inc(result);
|
|
end;
|
|
|
|
{ TButtonCellEditor }
|
|
|
|
procedure TButtonCellEditor.msg_SetGrid(var Msg: TGridMessage);
|
|
begin
|
|
FGrid:=Msg.Grid;
|
|
Msg.Options:=EO_HOOKKEYDOWN or EO_HOOKKEYPRESS or EO_HOOKKEYUP;
|
|
end;
|
|
|
|
procedure TButtonCellEditor.msg_SetBounds(var Msg: TGridMessage);
|
|
var
|
|
r: TRect;
|
|
begin
|
|
r := Msg.CellRect;
|
|
FGrid.AdjustInnerCellRect(r);
|
|
if r.Right-r.Left>DEFBUTTONWIDTH then
|
|
r.Left:=r.Right-DEFBUTTONWIDTH;
|
|
SetBounds(r.Left, r.Top, r.Right-r.Left, r.Bottom-r.Top);
|
|
end;
|
|
|
|
procedure TButtonCellEditor.msg_SetPos(var Msg: TGridMessage);
|
|
begin
|
|
FCol := Msg.Col;
|
|
FRow := Msg.Row;
|
|
end;
|
|
|
|
procedure TButtonCellEditor.msg_Ready(var Msg: TGridMessage);
|
|
begin
|
|
Width := DEFBUTTONWIDTH;
|
|
end;
|
|
|
|
procedure TButtonCellEditor.msg_GetGrid(var Msg: TGridMessage);
|
|
begin
|
|
Msg.Grid := FGrid;
|
|
Msg.Options:= EO_IMPLEMENTED;
|
|
end;
|
|
|
|
{ TPickListCellEditor }
|
|
procedure TPickListCellEditor.WndProc(var TheMessage: TLMessage);
|
|
begin
|
|
{$IfDef GridTraceMsg}
|
|
TransMsg('PicklistEditor: ', TheMessage);
|
|
{$Endif}
|
|
if TheMessage.msg=LM_KILLFOCUS then begin
|
|
if HWND(TheMessage.WParam) = HWND(Handle) then begin
|
|
// lost the focus but it returns to ourselves
|
|
// eat the message.
|
|
TheMessage.Result := 0;
|
|
exit;
|
|
end;
|
|
end;
|
|
inherited WndProc(TheMessage);
|
|
end;
|
|
|
|
procedure TPickListCellEditor.KeyDown(var Key: Word; Shift: TShiftState);
|
|
function AllSelected: boolean;
|
|
begin
|
|
result := (SelLength>0) and (SelLength=Length(Text));
|
|
end;
|
|
function AtStart: Boolean;
|
|
begin
|
|
Result:= (SelStart=0);
|
|
end;
|
|
function AtEnd: Boolean;
|
|
begin
|
|
result := ((SelStart+1)>Length(Text)) or AllSelected;
|
|
end;
|
|
procedure doEditorKeyDown;
|
|
begin
|
|
if FGrid<>nil then
|
|
FGrid.EditorkeyDown(Self, key, shift);
|
|
end;
|
|
procedure doGridKeyDown;
|
|
begin
|
|
if FGrid<>nil then
|
|
FGrid.KeyDown(Key, shift);
|
|
end;
|
|
function GetFastEntry: boolean;
|
|
begin
|
|
if FGrid<>nil then
|
|
Result := FGrid.FastEditing
|
|
else
|
|
Result := False;
|
|
end;
|
|
procedure CheckEditingKey;
|
|
begin
|
|
// if editor is not readonly, start editing
|
|
// else not interested
|
|
if (FGrid=nil) or FGrid.EditorIsReadOnly then
|
|
Key := 0;
|
|
end;
|
|
var
|
|
IntSel: boolean;
|
|
begin
|
|
{$IfDef dbgGrid}
|
|
DebugLn('TPickListCellEditor.KeyDown INIT: Key=',Dbgs(Key));
|
|
{$Endif}
|
|
inherited KeyDown(Key,Shift);
|
|
case Key of
|
|
|
|
VK_F2:
|
|
if AllSelected then begin
|
|
SelLength := 0;
|
|
SelStart := Length(Text);
|
|
end;
|
|
|
|
VK_RETURN:
|
|
if DroppedDown then begin
|
|
CheckEditingKey;
|
|
DroppedDown := False;
|
|
if Key<>0 then begin
|
|
doEditorKeyDown;
|
|
Key:=0;
|
|
end;
|
|
end else
|
|
doEditorKeyDown;
|
|
|
|
VK_DELETE:
|
|
CheckEditingKey;
|
|
|
|
VK_UP, VK_DOWN:
|
|
if not DroppedDown then
|
|
doGridKeyDown;
|
|
|
|
VK_LEFT, VK_RIGHT:
|
|
if GetFastEntry then begin
|
|
IntSel:=
|
|
((Key=VK_LEFT) and not AtStart) or
|
|
((Key=VK_RIGHT) and not AtEnd);
|
|
if not IntSel then begin
|
|
doGridKeyDown;
|
|
end;
|
|
end;
|
|
|
|
VK_END, VK_HOME:
|
|
;
|
|
VK_ESCAPE:
|
|
begin
|
|
doGridKeyDown;
|
|
FGrid.EditorHide;
|
|
end;
|
|
else
|
|
doEditorKeyDown;
|
|
end;
|
|
{$IfDef dbgGrid}
|
|
DebugLn('TPickListCellEditor.KeyDown END: Key=',Dbgs(Key));
|
|
{$Endif}
|
|
end;
|
|
|
|
procedure TPickListCellEditor.EditingDone;
|
|
begin
|
|
{$ifdef dbgGrid}DebugLn('TPickListCellEditor.EditingDone INIT');{$ENDIF}
|
|
inherited EditingDone;
|
|
if FGrid<>nil then
|
|
FGrid.EditingDone;
|
|
{$ifdef dbgGrid}DebugLn('TPickListCellEditor.EditingDone END');{$ENDIF}
|
|
end;
|
|
|
|
procedure TPickListCellEditor.DropDown;
|
|
begin
|
|
{$ifDef dbgGrid} DebugLn('TPickListCellEditor.DropDown INIT'); {$Endif}
|
|
inherited DropDown;
|
|
{$ifDef dbgGrid} DebugLn('TPickListCellEditor.DropDown END'); {$Endif}
|
|
end;
|
|
|
|
procedure TPickListCellEditor.CloseUp;
|
|
begin
|
|
{$ifDef dbgGrid} DebugLn('TPickListCellEditor.CloseUp INIT'); {$Endif}
|
|
inherited CloseUp;
|
|
{$ifDef dbgGrid} DebugLn('TPickListCellEditor.CloseUp END'); {$Endif}
|
|
end;
|
|
|
|
procedure TPickListCellEditor.Select;
|
|
begin
|
|
if FGrid<>nil then begin
|
|
FGrid.EditorTextChanged(FCol, FRow, Text);
|
|
FGrid.PickListItemSelected(Self);
|
|
end;
|
|
inherited Select;
|
|
end;
|
|
|
|
procedure TPickListCellEditor.Change;
|
|
begin
|
|
if FGrid<>nil then
|
|
FGrid.EditorTextChanged(FCol, FRow, Text);
|
|
inherited Change;
|
|
end;
|
|
|
|
procedure TPickListCellEditor.msg_GetValue(var Msg: TGridMessage);
|
|
begin
|
|
Msg.Col := FCol;
|
|
Msg.Row := FRow;
|
|
Msg.Value:=Text;
|
|
end;
|
|
|
|
procedure TPickListCellEditor.msg_SetGrid(var Msg: TGridMessage);
|
|
begin
|
|
FGrid:=Msg.Grid;
|
|
Msg.Options:=EO_AUTOSIZE or EO_SELECTALL or EO_HOOKKEYPRESS or EO_HOOKKEYUP;
|
|
end;
|
|
|
|
procedure TPickListCellEditor.msg_SetValue(var Msg: TGridMessage);
|
|
begin
|
|
Text := Msg.Value;
|
|
SelStart := Length(Text);
|
|
end;
|
|
|
|
procedure TPickListCellEditor.msg_SetPos(var Msg: TGridMessage);
|
|
begin
|
|
FCol := Msg.Col;
|
|
FRow := Msg.Row;
|
|
end;
|
|
|
|
procedure TPickListCellEditor.msg_GetGrid(var Msg: TGridMessage);
|
|
begin
|
|
Msg.Grid := FGrid;
|
|
Msg.Options:= EO_IMPLEMENTED;
|
|
end;
|
|
|
|
{ TCompositeCellEditor }
|
|
|
|
procedure TCompositeCellEditor.DispatchMsg(msg: TGridMessage);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to Length(FEditors)-1 do
|
|
if FEditors[i].Editor<>nil then
|
|
Feditors[i].Editor.Dispatch(msg);
|
|
end;
|
|
|
|
function TCompositeCellEditor.GetMaxLength: Integer;
|
|
var
|
|
AEditor: TWinControl;
|
|
begin
|
|
result := 0;
|
|
AEditor := GetActiveControl;
|
|
if AEditor is TCustomEdit then
|
|
result := TCustomEdit(AEditor).MaxLength;
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.SetMaxLength(AValue: Integer);
|
|
var
|
|
AEditor: TWinControl;
|
|
begin
|
|
AEditor := GetActiveControl;
|
|
if AEditor is TCustomEdit then
|
|
TCustomEdit(AEditor).MaxLength := AValue;
|
|
end;
|
|
|
|
function TCompositeCellEditor.GetActiveControl: TWinControl;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
result := nil;
|
|
for i:=0 to Length(Feditors)-1 do
|
|
if (FEditors[i].Editor<>nil) and
|
|
(FEditors[i].ActiveControl) then begin
|
|
Result := FEditors[i].Editor;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.msg_GetValue(var Msg: TGridMessage);
|
|
var
|
|
i: Integer;
|
|
DefaultValue: string;
|
|
LocalMsg: TGridMessage;
|
|
begin
|
|
Msg.Col := FCol;
|
|
Msg.Row := FRow;
|
|
|
|
DefaultValue := Msg.Value;
|
|
for i:=0 to Length(FEditors)-1 do begin
|
|
|
|
if FEditors[i].Editor=nil then
|
|
continue;
|
|
|
|
LocalMsg := Msg;
|
|
Feditors[i].Editor.Dispatch(LocalMsg);
|
|
if CompareText(DEfaultValue, LocalMsg.Value)<>0 then begin
|
|
// on multiple editors, simply return the first one has
|
|
// a different value than default value
|
|
Msg := LocalMsg;
|
|
break;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.msg_SetGrid(var Msg: TGridMessage);
|
|
var
|
|
LocalMsg,ResMsg: TGridMessage;
|
|
i: Integer;
|
|
begin
|
|
FGrid:=Msg.Grid;
|
|
ResMsg := Msg;
|
|
for i:=0 to Length(FEditors)-1 do begin
|
|
if FEditors[i].Editor=nil then
|
|
continue;
|
|
|
|
LocalMsg := Msg;
|
|
Feditors[i].Editor.Dispatch(LocalMsg);
|
|
|
|
if LocalMsg.Options and EO_SELECTALL <> 0 then
|
|
ResMsg.Options := ResMsg.Options or EO_SELECTALL;
|
|
if LocalMsg.Options and EO_HOOKKEYDOWN <> 0 then
|
|
ResMsg.Options := ResMsg.Options or EO_HOOKKEYDOWN;
|
|
if LocalMsg.Options and EO_HOOKKEYPRESS <> 0 then
|
|
ResMsg.Options := ResMsg.Options or EO_HOOKKEYPRESS;
|
|
if LocalMsg.Options and EO_HOOKKEYUP <> 0 then
|
|
ResMsg.Options := ResMsg.Options or EO_HOOKKEYUP;
|
|
|
|
end;
|
|
Msg := ResMsg;
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.msg_SetValue(var Msg: TGridMessage);
|
|
begin
|
|
DispatchMsg(msg);
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.msg_SetBounds(var Msg: TGridMessage);
|
|
var
|
|
r: TRect;
|
|
begin
|
|
r := Msg.CellRect;
|
|
FGrid.AdjustInnerCellRect(r);
|
|
SetBounds(r.Left, r.Top, r.Right-r.Left, r.Bottom-r.Top);
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.msg_SetMask(var Msg: TGridMessage);
|
|
begin
|
|
DispatchMsg(Msg);
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.msg_SelectAll(var Msg: TGridMessage);
|
|
begin
|
|
DispatchMsg(Msg);
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.CMControlChange(var Message: TLMEssage);
|
|
begin
|
|
if (Message.WParam<>0) and (not Boolean(Message.LParam)) then
|
|
TControl(Message.WParam).Align:=alNone;
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.msg_SetPos(var Msg: TGridMessage);
|
|
begin
|
|
FCol := Msg.Col;
|
|
FRow := Msg.Row;
|
|
DispatchMsg(Msg);
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.msg_GetGrid(var Msg: TGridMessage);
|
|
begin
|
|
Msg.Grid := FGrid;
|
|
Msg.Options:= EO_IMPLEMENTED;
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.VisibleChanging;
|
|
var
|
|
i: Integer;
|
|
Msg: TGridMessage;
|
|
begin
|
|
inherited VisibleChanging;
|
|
|
|
if Visible then begin
|
|
// hidding: hide all editors
|
|
for i:=0 to Length(Feditors)-1 do
|
|
if FEditors[i].Editor<>nil then
|
|
FEDitors[i].Editor.Visible:= not Visible;
|
|
end else begin
|
|
Msg.LclMsg.msg:=GM_READY;
|
|
// showing: show all editors
|
|
for i:=0 to Length(Feditors)-1 do begin
|
|
if FEditors[i].Editor=nil then
|
|
continue;
|
|
FEditors[i].Editor.Parent := Self;
|
|
FEditors[i].Editor.Visible:= True;
|
|
FEditors[i].Editor.Align:=FEditors[i].Align;
|
|
// notify now that it's now shown
|
|
FEditors[i].Editor.Dispatch(Msg);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.SetFocus;
|
|
var
|
|
ActCtrl: TWinControl;
|
|
begin
|
|
if Visible then begin
|
|
ActCtrl := GetActiveControl;
|
|
if ActCtrl<>nil then begin
|
|
ActCtrl.Visible:=true;
|
|
ActCtrl.SetFocus;
|
|
exit;
|
|
end;
|
|
end;
|
|
inherited SetFocus;
|
|
end;
|
|
|
|
function TCompositeCellEditor.Focused: Boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:=inherited Focused;
|
|
if not result then
|
|
for i:=0 to Length(Feditors)-1 do
|
|
if (FEditors[i].Editor<>nil) and (FEditors[i].Editor.Focused) then begin
|
|
result := true;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.WndProc(var TheMessage: TLMessage);
|
|
begin
|
|
with TheMessage do
|
|
if msg=LM_CHAR then begin
|
|
Result := SendChar(Char(WParam));
|
|
if Result=1 then
|
|
exit;
|
|
end;
|
|
inherited WndProc(TheMessage);
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.CustomAlignPosition(AControl: TControl;
|
|
var ANewLeft, ANewTop, ANewWidth, ANewHeight: Integer; var AlignRect: TRect;
|
|
AlignInfo: TAlignInfo);
|
|
begin
|
|
// Currently there is only one custom aligned control, so no provision is for
|
|
// calling CustomAlignInsertBefore() or share the space with other editors.
|
|
aNewLeft := 0;
|
|
aNewWidth := AlignRect.Width;
|
|
aNewTop := alignRect.Height div 2 - aNewHeight div 2;
|
|
end;
|
|
|
|
function TCompositeCellEditor.DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean;
|
|
begin
|
|
Result:=inherited DoUTF8KeyPress(UTF8Key);
|
|
if not Result and (Length(UTF8Key)>1) then begin
|
|
if SendChar(UTF8Key)=1 then begin
|
|
UTF8Key := '';
|
|
Result := true;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCompositeCellEditor.SendChar(AChar: TUTF8Char): Integer;
|
|
var
|
|
ActCtrl: TWinControl;
|
|
begin
|
|
Result := 0;
|
|
ActCtrl := GetActiveControl;
|
|
if (ActCtrl<>nil) and ActCtrl.HandleAllocated then begin
|
|
TWSCustomGridClass(FGrid.WidgetSetClass).SendCharToEditor(ActCtrl, AChar);
|
|
Result:=1;
|
|
end;
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.SetColor(Value: TColor);
|
|
var
|
|
activeCtrl: TWinControl;
|
|
begin
|
|
inherited SetColor(Value);
|
|
activeCtrl := ActiveControl;
|
|
if activeCtrl<>nil then
|
|
activeCtrl.Color := Value;
|
|
end;
|
|
|
|
destructor TCompositeCellEditor.Destroy;
|
|
begin
|
|
SetLength(FEditors, 0);
|
|
inherited destroy;
|
|
end;
|
|
|
|
procedure TCompositeCellEditor.AddEditor(aEditor: TWinControl; aAlign: TAlign;
|
|
ActiveCtrl: boolean);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i := Length(FEditors);
|
|
SetLength(FEditors, i+1);
|
|
FEditors[i].Editor := aEditor;
|
|
FEditors[i].Align := aAlign;
|
|
FEditors[i].ActiveControl:=ActiveCtrl;
|
|
end;
|
|
|
|
{ TStringGrid }
|
|
|
|
class procedure TStringGrid.WSRegisterClass;
|
|
const
|
|
Done: Boolean = False;
|
|
begin
|
|
if Done then
|
|
Exit;
|
|
RegisterPropertyToSkip(Self, 'VisibleRowCount',
|
|
'Property streamed in by older compiler', '');
|
|
RegisterPropertyToSkip(Self, 'VisibleColCount',
|
|
'Property streamed in by older compiler', '');
|
|
inherited WSRegisterClass;
|
|
Done := True;
|
|
end;
|
|
|
|
end.
|