
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8870 8e941d3f-bd1b-0410-a28a-d453659cc2b4
4240 lines
104 KiB
ObjectPascal
4240 lines
104 KiB
ObjectPascal
|
|
{-------------------------------------------------------------------------------
|
|
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
|
|
The Original Code is NiceGrid.pas released at April 11st, 2003.
|
|
The Original Code is a part of NiceGrid component.
|
|
The Initial Developer of the Original Code is Priyatna.
|
|
(Website: http://www.priyatna.org/ Email: me@priyatna.org)
|
|
All Rights Reserved.
|
|
|
|
Contributors:
|
|
- C. S. Phua <csphua@teledynamics.com.my>
|
|
|
|
|
|
Alternatively, the contents of this file may be used under the terms of the
|
|
GNU General Public License Version 2 or later (the "GPL"), in which case
|
|
the provisions of the GPL are applicable instead of those above.
|
|
If you wish to allow use of your version of this file only under the terms
|
|
of the GPL and not to allow others to use your version of this file
|
|
under the MPL, indicate your decision by deleting the provisions above and
|
|
replace them with the notice and other provisions required by the GPL.
|
|
If you do not delete the provisions above, a recipient may use your version
|
|
of this file under either the MPL or the GPL.
|
|
|
|
-------------------------------------------------------------------------------}
|
|
|
|
|
|
unit NiceGrid;
|
|
|
|
{$IFDEF FPC}
|
|
{$MODE Delphi}
|
|
{$ENDIF}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF FPC}
|
|
LCLIntf, LCLType, LMessages,
|
|
{$ELSE}
|
|
Windows, Messages,
|
|
{$ENDIF}
|
|
Forms, Controls, SysUtils, Types, Classes, Graphics, Contnrs,
|
|
StdCtrls, ExtCtrls, Clipbrd;
|
|
|
|
type
|
|
PHeaderInfo = ^THeaderInfo;
|
|
THeaderInfo = record
|
|
Str: string;
|
|
Rc: TRect;
|
|
end;
|
|
|
|
THorzAlign = (haLeft, haCenter, haRight);
|
|
TVertAlign = (vaTop, vaCenter, vaBottom);
|
|
TGutterKind = (gkNone, gkBlank, gkPointer, gkNumber, gkString);
|
|
TGridHittest = (gtNone, gtLeftTop, gtLeft, gtTop, gtCell, gtColSizing, gtSmallBox);
|
|
TNiceGridState = (gsNormal, gsSelAll, gsSelRow, gsSelCol, gsCell, gsColSize, gsBoxDrag);
|
|
|
|
TNiceGrid = class;
|
|
|
|
TNiceColumn = class(TCollectionItem)
|
|
private
|
|
FTitle: string;
|
|
FFooter: string;
|
|
FWidth: Integer;
|
|
FFont: TFont;
|
|
FColor: TColor;
|
|
FHorzAlign: THorzAlign;
|
|
FVertAlign: TVertAlign;
|
|
FVisible: Boolean;
|
|
FStrings: TStrings;
|
|
FTag: Integer;
|
|
FTag2: Integer;
|
|
FCanResize: Boolean;
|
|
FHint: string;
|
|
FReadOnly: Boolean;
|
|
function GetGrid: TNiceGrid;
|
|
function IsFontStored: Boolean;
|
|
procedure FontChange(Sender: TObject);
|
|
procedure SetTitle(Value: string);
|
|
procedure SetWidth(Value: Integer);
|
|
procedure SetFont(Value: TFont);
|
|
procedure SetColor(Value: TColor);
|
|
procedure SetHorzAlign(Value: THorzAlign);
|
|
procedure SetVertAlign(Value: TVertAlign);
|
|
procedure SetVisible(Value: Boolean);
|
|
procedure SetStrings(Value: TStrings);
|
|
procedure SetFooter(const Value: string);
|
|
protected
|
|
function GetDisplayName: string; override;
|
|
public
|
|
constructor Create(Collection: TCollection); override;
|
|
destructor Destroy; override;
|
|
procedure Assign(Source: TPersistent); override;
|
|
published
|
|
property Grid: TNiceGrid read GetGrid;
|
|
property Title: string read FTitle write SetTitle;
|
|
property Footer: string read FFooter write SetFooter;
|
|
property Width: Integer read FWidth write SetWidth;
|
|
property Font: TFont read FFont write SetFont stored IsFontStored;
|
|
property Color: TColor read FColor write SetColor default clWindow;
|
|
property HorzAlign: THorzAlign read FHorzAlign write SetHorzAlign default haLeft;
|
|
property VertAlign: TVertAlign read FVertAlign write SetVertAlign default vaCenter;
|
|
property Visible: Boolean read FVisible write SetVisible default True;
|
|
property Tag: Integer read FTag write FTag default 0;
|
|
property Tag2: Integer read FTag2 write FTag2 default 0;
|
|
property Hint: string read FHint write FHint;
|
|
property Strings: TStrings read FStrings write SetStrings;
|
|
property CanResize: Boolean read FCanResize write FCanResize default True;
|
|
property ReadOnly: Boolean read FReadOnly write FReadOnly default False;
|
|
end;
|
|
|
|
|
|
TNiceColumns = class(TCollection)
|
|
private
|
|
FGrid: TNiceGrid;
|
|
function GetItem(Index: Integer): TNiceColumn;
|
|
procedure SetItem(Index: Integer; Value: TNiceColumn);
|
|
protected
|
|
function GetOwner: TPersistent; override;
|
|
procedure Update(Item: TCollectionItem); override;
|
|
public
|
|
constructor Create(AGrid: TNiceGrid);
|
|
property Grid: TNiceGrid read FGrid;
|
|
property Items[Index: Integer]: TNiceColumn read GetItem write SetItem; default;
|
|
function Add: TNiceColumn;
|
|
function AddItem(Item: TNiceColumn; Index: Integer): TNiceColumn;
|
|
function Insert(Index: Integer): TNiceColumn;
|
|
end;
|
|
|
|
|
|
TNiceInplace = class(TEdit)
|
|
private
|
|
FGrid: TNiceGrid;
|
|
FAlignment: THorzAlign;
|
|
CellX, CellY: Integer;
|
|
procedure SetAlignment(Value: THorzAlign);
|
|
protected
|
|
procedure CreateParams(var Params: TCreateParams); override;
|
|
procedure Change; override;
|
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
|
procedure KeyPress(var Key: Char); override;
|
|
public
|
|
constructor Create(Grid: TNiceGrid); reintroduce;
|
|
procedure ShowEdit(X, Y: Integer);
|
|
procedure HideEdit;
|
|
end;
|
|
|
|
TMergeCell = class(TObject)
|
|
public
|
|
Caption: string;
|
|
Rc: TRect;
|
|
Color: TColor;
|
|
Font: TFont;
|
|
HorzAlign: THorzAlign;
|
|
VertAlign: TVertAlign;
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TOnDrawCellEvent = procedure (Sender: TObject; ACanvas: TCanvas; X, Y: Integer;
|
|
Rc: TRect; var Handled: Boolean) of object;
|
|
|
|
TOnDrawHeaderEvent = procedure (Sender: TObject; ACanvas: TCanvas; Rc: TRect;
|
|
Str: string; var Handled: Boolean) of object;
|
|
|
|
TOnFilterChar = procedure (Sender: TObject; Col: Integer; Row: Integer;
|
|
Chr: Char; var Allowed: Boolean) of object;
|
|
|
|
TOnHeaderClick = procedure (Sender: TObject; Col: Integer;
|
|
Button: TMouseButton; Shift: TShiftState) of object;
|
|
|
|
TOnGutterClick = procedure (Sender: TObject; Row: Integer;
|
|
Button: TMouseButton; Shift: TShiftState) of object;
|
|
|
|
TOnCellAssignment = procedure (Sender: TObject; Col, Row: Integer;
|
|
var Str: string) of object;
|
|
|
|
TOnCellChange = procedure (Sender: TObject; Col, Row: Integer; var Str: string)
|
|
of object;
|
|
|
|
TOnCellChanging = procedure (Sender: TObject; Col, Row: Integer;
|
|
var CanChange: Boolean) of object;
|
|
|
|
TOnRowEvent = procedure (Sender: TObject; ARow: Integer) of object;
|
|
|
|
TOnColRowChanged = procedure (Sender: TObject; Col, Row: Integer) of object;
|
|
|
|
TNiceGridSync = class;
|
|
|
|
TNiceGrid = class(TCustomPanel)
|
|
private
|
|
ForcedColumn: Integer;
|
|
FixedWidth, FixedHeight: Integer;
|
|
BodyWidth, BodyHeight: Integer;
|
|
AllWidth, AllHeight: Integer;
|
|
FooterTop: Integer;
|
|
CellBox: TRect;
|
|
|
|
FHorzOffset: Integer;
|
|
FVertOffset: Integer;
|
|
FMaxHScroll: Integer;
|
|
FMaxVScroll: Integer;
|
|
FSmallChange: Integer;
|
|
FLargeChange: Integer;
|
|
|
|
FAutoAddRow: Boolean;
|
|
FRowCount: Integer;
|
|
FDefRowHeight: Integer;
|
|
FDefColWidth: Integer;
|
|
FFlat: Boolean;
|
|
FGridState: TNiceGridState;
|
|
|
|
FHeaderLine: Integer;
|
|
FHeaderInfos: TList;
|
|
FUpdating: Boolean;
|
|
FColor: TColor;
|
|
FAlternateColor: TColor;
|
|
FGridColor: TColor;
|
|
FShowGrid: Boolean;
|
|
FHeaderColor: TColor;
|
|
FHeaderLightColor: TColor;
|
|
FHeaderDarkColor: TColor;
|
|
FSelectionColor: TColor;
|
|
FHeaderFont: TFont;
|
|
FGutterFont: TFont;
|
|
|
|
FGutterKind: TGutterKind;
|
|
FGutterWidth: Integer;
|
|
|
|
FFitToWidth: Boolean;
|
|
FAutoColWidth: Boolean;
|
|
FReadOnly: Boolean;
|
|
FColumns: TNiceColumns;
|
|
|
|
ValidationEnabled: Boolean;
|
|
FEdit: TNiceInplace;
|
|
FCol: Integer;
|
|
FRow: Integer;
|
|
FCol2, FRow2: Integer; // Selection
|
|
FSelectArea: TRect;
|
|
|
|
SmallBox: TRect;
|
|
SmallBoxArea: TRect;
|
|
SmallBoxPos: Byte;
|
|
|
|
BuffString: string;
|
|
IsEditing: Boolean;
|
|
SizingCol: Integer;
|
|
SizingColX: Integer;
|
|
LastHover: Integer;
|
|
Sync: TNiceGridSync;
|
|
Mergeds: TList;
|
|
|
|
FOnDrawCell: TOnDrawCellEvent;
|
|
FOnDrawHeader: TOnDrawHeaderEvent;
|
|
FOnDrawGutter: TOnDrawHeaderEvent;
|
|
FOnDrawFooter: TOnDrawHeaderEvent;
|
|
FOnFilterChar: TOnFilterChar;
|
|
FOnHeaderClick: TOnHeaderClick;
|
|
FOnGutterClick: TOnGutterClick;
|
|
FOnCellChange: TOnCellChange;
|
|
FOnCellChanging: TOnCellChanging;
|
|
FOnColRowChanged: TOnColRowChanged;
|
|
FOnInsertRow: TOnRowEvent;
|
|
FOnDeleteRow: TOnRowEvent;
|
|
FOnCellAssignment: TOnCellAssignment;
|
|
FGutterStrings: TStrings;
|
|
FShowFooter: Boolean;
|
|
FFooterFont: TFont;
|
|
FEnabled: Boolean;
|
|
FAutoFillRight: Boolean;
|
|
FAutoFillDown: Boolean;
|
|
|
|
{$IFDEF FPC}
|
|
procedure WMUnknown(var Msg: TLMessage); message LM_USER + $B902;
|
|
procedure WMVScroll(var Msg: TLMVScroll); message LM_VSCROLL;
|
|
procedure WMHScroll(var Msg: TLMHScroll); message LM_HSCROLL;
|
|
procedure WMMouseWheel(var Msg: TLMessage{TWMMouseWheel}); message LM_MOUSEWHEEL;
|
|
procedure WMSize(var Msg: TLMessage); message LM_SIZE;
|
|
procedure WMEraseBkgnd(var Msg: TLMEraseBkGnd); message LM_ERASEBKGND;
|
|
procedure WMSetFocus(var Msg: TLMSetFocus); message LM_SETFOCUS;
|
|
procedure WMKillFocus(var Msg: TLMKillFocus); message LM_KILLFOCUS;
|
|
procedure CMWantSpecialKey(var Message: TLMKey); message CM_WANTSPECIALKEY;
|
|
procedure CMFontChanged(var Msg: TLMessage); message CM_FONTCHANGED;
|
|
{$ELSE}
|
|
procedure WMUnknown(var Msg: TMessage); message WM_USER + $B902;
|
|
procedure WMVScroll(var Msg: TWMVScroll); message WM_VSCROLL;
|
|
procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL;
|
|
procedure WMMouseWheel(var Msg: TWMMouseWheel); message WM_MOUSEWHEEL;
|
|
procedure WMSize(var Msg: TMessage); message WM_SIZE;
|
|
procedure WMEraseBkgnd(var Msg: TWMEraseBkGnd); message WM_ERASEBKGND;
|
|
procedure WMSetFocus(var Msg: TWMSetFocus); message WM_SETFOCUS;
|
|
procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS;
|
|
procedure CMWantSpecialKey(var Message: TWMKey); message CM_WANTSPECIALKEY;
|
|
procedure CMFontChanged(var Msg: TMessage); message CM_FONTCHANGED;
|
|
{$ENDIF}
|
|
|
|
function TotalWidth: Integer;
|
|
procedure ClearHeaderInfos;
|
|
|
|
procedure ClearUnused;
|
|
procedure RenderGutter;
|
|
procedure RenderHeader;
|
|
procedure DrawSelection;
|
|
|
|
procedure SetHorzOffset(Value: Integer);
|
|
procedure SetVertOffset(Value: Integer);
|
|
function GetColCount: Integer;
|
|
procedure SetColCount(Value: Integer);
|
|
procedure SetRowCount(Value: Integer);
|
|
procedure SetDefColWidth(Value: Integer);
|
|
procedure SetDefRowHeight(Value: Integer);
|
|
procedure SetFlat(Value: Boolean);
|
|
procedure SetColor(Value: TColor); reintroduce;
|
|
procedure SetAlternateColor(Value: TColor);
|
|
procedure SetGridColor(Value: TColor);
|
|
procedure SetShowGrid(Value: Boolean);
|
|
procedure SetHeaderLine(Value: Integer);
|
|
procedure SetHeaderColor(Value: TColor);
|
|
procedure SetHeaderLightColor(Value: TColor);
|
|
procedure SetHeaderDarkColor(Value: TColor);
|
|
procedure SetHeaderFont(Value: TFont);
|
|
procedure SetSelectionColor(Value: TColor);
|
|
procedure SetFitToWidth(Value: Boolean);
|
|
procedure SetAutoColWidth(Value: Boolean);
|
|
procedure SetReadOnly(Value: Boolean);
|
|
procedure InternalSetCell(X, Y: Integer; Value: string; FireOnChange: Boolean);
|
|
procedure SetCell(X, Y: Integer; Value: string);
|
|
function GetColWidths(Index: Integer): Integer;
|
|
procedure SetColWidths(Index: Integer; Value: Integer);
|
|
procedure SetColumns(Value: TNiceColumns);
|
|
procedure SetCol(Value: Integer);
|
|
procedure SetRow(Value: Integer);
|
|
procedure AdjustSelection(Value: TRect; Force: Boolean);
|
|
procedure SetSelectArea(Value: TRect);
|
|
procedure SetGutterKind(Value: TGutterKind);
|
|
procedure SetGutterWidth(Value: Integer);
|
|
procedure SetGutterFont(Value: TFont);
|
|
procedure HeaderFontChange(Sender: TObject);
|
|
procedure GutterFontChange(Sender: TObject);
|
|
|
|
function CreateColumn: TNiceColumn;
|
|
procedure UpdateColumn(Index: Integer);
|
|
procedure UpdateColumns;
|
|
procedure UpdateHeader;
|
|
|
|
function GetCellRect(x, y: Integer): TRect;
|
|
function CellRectToClient(R: TRect): TRect;
|
|
function GetCellAtPos(X, Y: Integer): TPoint;
|
|
function GetColFromX(X: Integer): Integer;
|
|
function GetRowFromY(Y: Integer): Integer;
|
|
function GetColCoord(I: Integer): Integer;
|
|
function GetCell(X, Y: Integer): string;
|
|
function SafeGetCell(X, Y: Integer): string;
|
|
function GetCellColor(X, Y: Integer): TColor;
|
|
procedure DrawCell(X, Y: Integer);
|
|
procedure InvalidateCell(X, Y: Integer);
|
|
function FastDrawCell(X, Y: Integer; IsEditing: Boolean): TPoint;
|
|
procedure ForceHideCaret;
|
|
procedure ForceShowCaret;
|
|
procedure NormalizeVertOffset;
|
|
procedure InvalidateCells;
|
|
procedure InvalidateRightWard(Left: Integer);
|
|
procedure InvalidateDownWard(Top: Integer);
|
|
procedure InvalidateHeader;
|
|
procedure InvalidateGutter;
|
|
function GetFirstVisible: Integer;
|
|
function GetLastVisible: Integer;
|
|
function GetNextVisible(Index: Integer): Integer;
|
|
function GetPrevVisible(Index: Integer): Integer;
|
|
procedure ColRowChanged;
|
|
procedure SetGutterStrings(const Value: TStrings);
|
|
function GetObject(X, Y: Integer): TObject;
|
|
procedure SetObject(X, Y: Integer; const Value: TObject);
|
|
procedure BuildMergeData;
|
|
procedure DrawMergedCell(Index: Integer);
|
|
procedure SetShowFooter(const Value: Boolean);
|
|
procedure RenderFooter;
|
|
procedure SetFooterFont(const Value: TFont);
|
|
procedure FooterFontChange(Sender: TObject);
|
|
procedure DrawFixCell(Rc: TRect; Str: string; AFont: TFont; AEvent: TOnDrawHeaderEvent);
|
|
procedure SetEnabled(const Value: Boolean); reintroduce;
|
|
|
|
protected
|
|
function GetMergedCellsData: TList;
|
|
function GetHeaderInfo: TList;
|
|
procedure SetScrollBar(AKind, AMax, APos, AMask: Integer); virtual;
|
|
procedure ShowHideScrollBar(HorzVisible, VertVisible: Boolean); virtual;
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
|
procedure Recalculate; virtual;
|
|
procedure CreateWnd; override;
|
|
procedure CreateParams(var Params: TCreateParams); override;
|
|
procedure Paint; override;
|
|
procedure Loaded; override;
|
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
|
|
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
|
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
|
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
|
procedure KeyPress(var Key: Char); override;
|
|
function TextExtent(const s: String): TSize;
|
|
{$IFDEF FPC}
|
|
procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy; const AXProportion, AYProportion: Double); override;
|
|
{$ENDIF}
|
|
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure BeginUpdate;
|
|
procedure EndUpdate;
|
|
procedure Clear;
|
|
property Cells[X, Y: Integer]: string read GetCell write SetCell; default;
|
|
property Objects[X, Y: Integer]: TObject read GetObject write SetObject;
|
|
property ColWidths[Index: Integer]: Integer read GetColWidths write SetColWidths;
|
|
procedure EnsureVisible(X, Y: Integer); overload;
|
|
procedure CutToClipboard;
|
|
procedure CopyToClipboard;
|
|
procedure PasteFromClipboard;
|
|
function GetHitTestInfo(X, Y: Integer): TGridHitTest;
|
|
function HeaderCellsCount: Integer;
|
|
function HeaderCells(I: Integer): THeaderInfo;
|
|
property Col: Integer read FCol write SetCol;
|
|
property Row: Integer read FRow write SetRow;
|
|
property SelectArea: TRect read FSelectArea write SetSelectArea;
|
|
procedure DeleteRow(ARow: Integer);
|
|
procedure InsertRow(ARow: Integer);
|
|
function AddRow: Integer;
|
|
property HorzOffset: Integer read FHorzOffset write SetHorzOffset;
|
|
property VertOffset: Integer read FVertOffset write SetVertOffset;
|
|
function MergeCells(const X1, Y1, X2, Y2: Integer; ACaption: string): TMergeCell;
|
|
procedure ClearMergeCells;
|
|
{$IFDEF FPC}
|
|
procedure FixDesignFontsPPI(const ADesignTimePPI: Integer); override;
|
|
procedure ScaleFontsPPI(const AToPPI: Integer; const AProportion: Double); override;
|
|
{$ENDIF}
|
|
|
|
published
|
|
property Enabled: Boolean read FEnabled write SetEnabled default True;
|
|
property ColCount: Integer read GetColCount write SetColCount;
|
|
property RowCount: Integer read FRowCount write SetRowCount default 5;
|
|
property AutoAddRow: Boolean read FAutoAddRow write FAutoAddRow default False;
|
|
property AutoFillDown: Boolean read FAutoFillDown write FAutoFillDown default False;
|
|
property AutoFillRight: Boolean read FAutoFillRight write FAutoFillRight default False;
|
|
property DefRowHeight: Integer read FDefRowHeight write SetDefRowHeight default 18;
|
|
property DefColWidth: Integer read FDefColWidth write SetDefColWidth default 80;
|
|
property Flat: Boolean read FFlat write SetFlat default True;
|
|
property Color: TColor read FColor write SetColor default clWindow;
|
|
property AlternateColor: TColor read FAlternateColor write SetAlternateColor default clWindow;
|
|
property GridColor: TColor read FGridColor write SetGridColor default clBtnFace;
|
|
property ShowGrid: Boolean read FShowGrid write SetShowGrid default True;
|
|
property HeaderLine: Integer read FHeaderLine write SetHeaderLine default 1;
|
|
property HeaderColor: TColor read FHeaderColor write SetHeaderColor default clBtnFace;
|
|
property HeaderLightColor: TColor read FHeaderLightColor write SetHeaderLightColor default clBtnHighlight;
|
|
property HeaderDarkColor: TColor read FHeaderDarkColor write SetHeaderDarkColor default clBtnShadow;
|
|
property HeaderFont: TFont read FHeaderFont write SetHeaderFont;
|
|
property FooterFont: TFont read FFooterFont write SetFooterFont;
|
|
property SelectionColor: TColor read FSelectionColor write SetSelectionColor default $00CAFFFF;
|
|
property FitToWidth: Boolean read FFitToWidth write SetFitToWidth default False;
|
|
property AutoColWidth: Boolean read FAutoColWidth write SetAutoColWidth default False;
|
|
property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
|
|
property Columns: TNiceColumns read FColumns write SetColumns;
|
|
property GutterKind: TGutterKind read FGutterKind write SetGutterKind default gkBlank;
|
|
property GutterWidth: Integer read FGutterWidth write SetGutterWidth default 20;
|
|
property GutterFont: TFont read FGutterFont write SetGutterFont;
|
|
property GutterStrings: TStrings read FGutterStrings write SetGutterStrings;
|
|
property ShowFooter: Boolean read FShowFooter write SetShowFooter;
|
|
property OnDrawCell: TOnDrawCellEvent read FOnDrawCell write FOnDrawCell;
|
|
property OnDrawHeader: TOnDrawHeaderEvent read FOnDrawHeader write FOnDrawHeader;
|
|
property OnDrawGutter: TOnDrawHeaderEvent read FOnDrawGutter write FOnDrawGutter;
|
|
property OnDrawFooter: TOnDrawHeaderEvent read FOnDrawFooter write FOnDrawFooter;
|
|
property OnFilterChar: TOnFilterChar read FOnFilterChar write FOnFilterChar;
|
|
property OnHeaderClick: TOnHeaderClick read FOnHeaderClick write FOnHeaderClick;
|
|
property OnGutterClick: TOnGutterClick read FOnGutterClick write FOnGutterClick;
|
|
property OnCellChange: TOnCellChange read FOnCellChange write FOnCellChange;
|
|
property OnCellChanging: TOnCellChanging read FOnCellChanging write FOnCellChanging;
|
|
property OnColRowChanged: TOnColRowChanged read FOnColRowChanged write FOnColRowChanged;
|
|
property OnInsertRow: TOnRowEvent read FOnInsertRow write FOnInsertRow;
|
|
property OnDeleteRow: TOnRowEvent read FOnDeleteRow write FOnDeleteRow;
|
|
property OnCellAssignment: TOnCellAssignment read FOnCellAssignment write FOnCellAssignment;
|
|
property Font;
|
|
property Anchors;
|
|
property Align;
|
|
{$IFDEF FPC}
|
|
property BorderSpacing;
|
|
{$ELSE}
|
|
property BevelKind;
|
|
{$ENDIF}
|
|
property BorderStyle default bsSingle;
|
|
property BevelOuter default bvNone;
|
|
property BevelInner;
|
|
property TabOrder;
|
|
property TabStop default True;
|
|
property Tag;
|
|
property OnClick;
|
|
property OnDblClick;
|
|
property OnMouseDown;
|
|
property OnMouseMove;
|
|
property OnMouseUp;
|
|
property OnKeyPress;
|
|
property OnKeyDown;
|
|
property OnKeyUp;
|
|
property PopupMenu;
|
|
end;
|
|
|
|
TNiceGridSync = class(TNiceGrid)
|
|
private
|
|
FGrid: TNiceGrid;
|
|
procedure SetGrid(const Value: TNiceGrid);
|
|
procedure SyncDeleteRow(Sender: TObject; ARow: Integer);
|
|
procedure SyncInsertRow(Sender: TObject; ARow: Integer);
|
|
procedure SyncColRow(Sender: TObject; ACol, ARow: Integer);
|
|
protected
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
|
procedure SetScrollBar(AKind, AMax, APos, AMask: Integer); override;
|
|
procedure ShowHideScrollBar(HorzVisible, VertVisible: Boolean); override;
|
|
property OnDeleteRow;
|
|
property OnInsertRow;
|
|
property OnColRowChanged;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
published
|
|
property Grid: TNiceGrid read FGrid write SetGrid;
|
|
end;
|
|
|
|
|
|
function DrawString(Canvas: TCanvas; Str: string; Rc: TRect;
|
|
HorzAlign: THorzAlign; VertAlign: TVertAlign; IsEditing: Boolean): TPoint;
|
|
|
|
procedure DrawStringMulti(Canvas: TCanvas; Str: string; Rc: TRect;
|
|
HorzAlign: THorzAlign; VertAlign: TVertAlign);
|
|
|
|
|
|
implementation
|
|
|
|
{$R NiceCursors.res}
|
|
|
|
uses
|
|
Math;
|
|
|
|
const
|
|
crPlus = 101;
|
|
crSmallCross = 102;
|
|
crRight = 103;
|
|
crDown = 104;
|
|
crLeftTop = 105;
|
|
|
|
CursorArray: array [TGridHitTest] of TCursor =
|
|
//(gtNone, gtLeftTop, gtLeft, gtTop, gtCell, gtColSizing, gtSmallBox);
|
|
{$IFDEF LCLGtk3} // Issue with loading cursors in GTK3 --> use predefined cursors.
|
|
(crDefault, crSizeSE, crSizeE, crSizeS, crHandPoint, crHSplit, crCross);
|
|
{$ELSE}
|
|
(crDefault, crLeftTop, crRight, crDown, crPlus, crHSplit, crSmallCross);
|
|
{$ENDIF}
|
|
|
|
GridStateArray: array[TGridHitTest] of TNiceGridState =
|
|
(gsNormal, gsSelAll, gsSelRow, gsSelCol, gsCell, gsColSize, gsBoxDrag);
|
|
|
|
MergeID = -2;
|
|
|
|
{$HINTS OFF}
|
|
procedure Unused(const A1); overload;
|
|
begin
|
|
end;
|
|
|
|
procedure Unused(const A1, A2); overload;
|
|
begin
|
|
end;
|
|
{$HINTS ON}
|
|
|
|
{ TNiceGrid }
|
|
|
|
constructor TNiceGrid.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
Width := 200;
|
|
Height := 200;
|
|
inherited Color := clWindow;
|
|
BevelOuter := bvNone;
|
|
BorderStyle := bsSingle;
|
|
TabStop := True;
|
|
TabOrder := 0;
|
|
ParentColor := False;
|
|
ParentBackground := False;
|
|
ParentFont := False;
|
|
|
|
{$IFDEF VER150}
|
|
ControlStyle := ControlStyle + [csNeedsBorderPaint];
|
|
{$ENDIF}
|
|
|
|
FFlat := True;
|
|
FEnabled := True;
|
|
FColor := clWindow;
|
|
FAlternateColor := clWindow;
|
|
FGridColor := clBtnFace;
|
|
FShowGrid := True;
|
|
FHeaderColor := clBtnface;
|
|
FHeaderLightColor := clBtnHighlight;
|
|
FHeaderDarkColor := clBtnShadow;
|
|
FHeaderFont := TFont.Create;
|
|
FHeaderFont.OnChange := HeaderFontChange;
|
|
FSelectionColor := $00CAFFFF;
|
|
|
|
FFooterFont := TFont.Create;
|
|
FFooterFont.OnChange := FooterFontChange;
|
|
|
|
FDefRowHeight := 18;
|
|
FDefColWidth := 80;
|
|
FRowCount := 5;
|
|
FAutoAddRow := False;
|
|
FGutterKind := gkBlank;
|
|
FGutterWidth := 20;
|
|
FGutterFont := TFont.Create;
|
|
FGutterFont.OnChange := GutterFontChange;
|
|
|
|
FHorzOffset := 0;
|
|
FVertOffset := 0;
|
|
FMaxHScroll := 0;
|
|
FMaxVScroll := 0;
|
|
FSmallChange := FDefRowHeight;
|
|
FLargeChange := FDefRowHeight * 5;
|
|
ForcedColumn := -1;
|
|
AllWidth := 200;
|
|
AllHeight := 200;
|
|
|
|
FHeaderLine := 1;
|
|
FHeaderInfos := TList.Create;
|
|
|
|
ValidationEnabled := True;
|
|
CellBox := Rect(0, 0, 0, 0);
|
|
FCol := 0;
|
|
FRow := 0;
|
|
FCol2 := 0;
|
|
FRow2 := 0;
|
|
FSelectArea := Rect(0, 0, 0, 0);
|
|
IsEditing := False;
|
|
BuffString := '';
|
|
SmallBox := Rect(-1, -1, -1, -1);
|
|
SmallBoxArea := Rect(-1, -1, -1, -1);
|
|
SmallBoxPos := 0;
|
|
SizingCol := -1;
|
|
SizingColX := -1;
|
|
|
|
{$IFNDEF LCLGtk3} // Issue with loading cursors in GTK3.
|
|
Screen.Cursors[crPlus] := LoadCursor(hinstance, 'CR_PLUS');
|
|
Screen.Cursors[crSmallCross] := LoadCursor(hInstance, 'CR_CROSS');
|
|
Screen.Cursors[crRight] := LoadCursor(hinstance, 'CR_RIGHT');
|
|
Screen.Cursors[crDown] := LoadCursor(hinstance, 'CR_DOWN');
|
|
Screen.Cursors[crLeftTop] := LoadCursor(hinstance, 'CR_LEFTTOP');
|
|
{$ENDIF}
|
|
Cursor := crPlus;
|
|
FGridState := gsNormal;
|
|
|
|
FColumns := TNiceColumns.Create(Self);
|
|
FEdit := TNiceInplace.Create(Self);
|
|
FGutterStrings := TStringList.Create;
|
|
Mergeds := TList.Create;
|
|
|
|
end;
|
|
|
|
destructor TNiceGrid.Destroy;
|
|
begin
|
|
ClearMergeCells;
|
|
Mergeds.Free;
|
|
FGutterStrings.Free;
|
|
FEdit.Free;
|
|
FColumns.Free;
|
|
ClearHeaderInfos;
|
|
FHeaderInfos.Free;
|
|
FHeaderFont.Free;
|
|
FFooterFont.Free;
|
|
FGutterFont.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TNiceGrid.CreateParams(var Params: TCreateParams);
|
|
begin
|
|
inherited CreateParams(Params);
|
|
Params.Style := Params.Style or WS_HSCROLL or WS_VSCROLL;
|
|
end;
|
|
|
|
procedure TNiceGrid.CreateWnd;
|
|
begin
|
|
inherited CreateWnd;
|
|
ShowHideScrollBar(False, False);
|
|
Recalculate;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetScrollBar(AKind, AMax, APos, AMask: Integer);
|
|
var Info: TScrollInfo;
|
|
begin
|
|
{$IFDEF FPC}
|
|
Info := Default(TScrollInfo);
|
|
{$ELSE}
|
|
FillChar(Info, SizeOf(TScrollInfo), 0);
|
|
{$ENDIF}
|
|
Info.cbSize := SizeOf(TScrollInfo);
|
|
Info.nMin := 0;
|
|
Info.nMax := AMax;
|
|
Info.nPos := APos;
|
|
Info.fMask := AMask;
|
|
SetScrollInfo(Handle, AKind, Info, TRUE);
|
|
if (AKind = SB_VERT) and Assigned(Sync) then
|
|
begin
|
|
if ((AMask and SIF_RANGE) <> 0)
|
|
then Sync.FMaxVScroll := AMax;
|
|
if ((AMask and SIF_POS) <> 0)
|
|
then Sync.VertOffset := APos;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.ShowHideScrollBar(HorzVisible, VertVisible: Boolean);
|
|
begin
|
|
ShowScrollBar(Handle, SB_HORZ, HorzVisible);
|
|
ShowScrollBar(Handle, SB_VERT, VertVisible);
|
|
end;
|
|
|
|
procedure TNiceGrid.WMHScroll(var Msg: {$IFDEF FPC}TLMVScroll{$ELSE}TWMVScroll{$ENDIF});
|
|
var
|
|
Old: Integer;
|
|
begin
|
|
ForceHideCaret;
|
|
Old := FHorzOffset;
|
|
case Msg.ScrollCode of
|
|
SB_LINELEFT:
|
|
FHorzOffset := FHorzOffset - FSmallChange;
|
|
SB_LINERIGHT:
|
|
FHorzOffset := FHorzOffset + FSmallChange;
|
|
SB_PAGELEFT:
|
|
FHorzOffset := FHorzOffset - FLargeChange;
|
|
SB_PAGERIGHT:
|
|
FHorzOffset := FHorzOffset + FLargeChange;
|
|
SB_THUMBTRACK:
|
|
FHorzOffset := Msg.Pos;
|
|
SB_THUMBPOSITION:
|
|
FHorzOffset := Msg.Pos;
|
|
end;
|
|
FHorzOffset := Max(0, Min(FMaxHScroll, FHorzOffset));
|
|
if (FHorzOffset <> Old) then
|
|
begin
|
|
SetScrollBar(SB_HORZ, 0, FHorzOffset, SIF_POS);
|
|
InvalidateRightWard(FixedWidth);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.WMVScroll(var Msg: {$IFDEF FPC}TLMHScroll{$ELSE}TWMHScroll{$ENDIF});
|
|
var
|
|
Old: Integer;
|
|
begin
|
|
ForceHideCaret;
|
|
Old := FVertOffset;
|
|
case Msg.ScrollCode of
|
|
SB_LINEUP:
|
|
FVertOffset := FVertOffset - FSmallChange;
|
|
SB_LINEDOWN:
|
|
FVertOffset := FVertOffset + FSmallChange;
|
|
SB_PAGEUP:
|
|
FVertOffset := FVertOffset - FLargeChange;
|
|
SB_PAGEDOWN:
|
|
FVertOffset := FVertOffset + FLargeChange;
|
|
SB_THUMBTRACK:
|
|
FVertOffset := Msg.Pos;
|
|
SB_THUMBPOSITION:
|
|
FVertOffset := Msg.Pos;
|
|
end;
|
|
FVertOffset := Max(0, Min(FMaxVScroll, FVertOffset));
|
|
NormalizeVertOffset;
|
|
if (FVertOffset <> Old) then
|
|
begin
|
|
SetScrollBar(SB_VERT, 0, FVertOffset, SIF_POS);
|
|
InvalidateDownWard(FixedHeight);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetColCount(Value: Integer);
|
|
begin
|
|
if (ColCount <> Value) then
|
|
begin
|
|
FColumns.BeginUpdate;
|
|
while (ColCount > Value)
|
|
do FColumns.Delete(FColumns.Count-1);
|
|
while (ColCount < Value)
|
|
do FColumns.Add;
|
|
FHorzOffset := 0;
|
|
FVertOffset := 0;
|
|
FCol := Max(0, Min(FCol, ColCount-1));
|
|
FRow := Max(0, Min(FRow, FRowCount-1));
|
|
if (FRowCount = 0) or (ColCount = 0) then
|
|
begin
|
|
FCol := -1;
|
|
FRow := -1;
|
|
end;
|
|
FSelectArea := Rect(FCol, FRow, FCol, FRow);
|
|
FColumns.EndUpdate;
|
|
ColRowChanged;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetRowCount(Value: Integer);
|
|
begin
|
|
if (FRowCount <> Value) then
|
|
begin
|
|
FRowCount := Value;
|
|
FCol := Max(0, Min(FCol, ColCount-1));
|
|
FRow := Max(0, Min(FRow, FRowCount-1));
|
|
if (FRowCount = 0) or (ColCount = 0) then
|
|
begin
|
|
FCol := -1;
|
|
FRow := -1;
|
|
end;
|
|
FSelectArea := Rect(FCol, FRow, FCol, FRow);
|
|
Recalculate;
|
|
Invalidate;
|
|
ColRowChanged;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.ClearHeaderInfos;
|
|
var
|
|
x: Integer;
|
|
P: PHeaderInfo;
|
|
begin
|
|
for x := 0 to FHeaderInfos.Count-1 do
|
|
begin
|
|
P := PHeaderInfo(FHeaderInfos[x]);
|
|
Dispose(P);
|
|
end;
|
|
FHeaderInfos.Clear;
|
|
end;
|
|
|
|
procedure TNiceGrid.Recalculate;
|
|
var
|
|
x: Integer;
|
|
HVisible, VVisible: Boolean;
|
|
VisCount: Integer;
|
|
WidthAvail, HeightAvail: Integer;
|
|
v: Integer;
|
|
LastBodyWidth: Integer;
|
|
bmp: TBitmap;
|
|
lCanvas: TCanvas;
|
|
|
|
function GetColAutoWidth(i: Integer): Integer;
|
|
var
|
|
n: Integer;
|
|
t: TStrings;
|
|
begin
|
|
Result := 0;
|
|
t := Columns[i].FStrings;
|
|
for n := 0 to t.Count-1 do
|
|
Result := Max(Result, lCanvas.TextWidth(t[n]) + 7);
|
|
Result := Max(Result, 20);
|
|
end;
|
|
|
|
begin
|
|
if csLoading in ComponentState then
|
|
exit;
|
|
|
|
BuildMergeData;
|
|
|
|
if Canvas.HandleAllocated then
|
|
begin
|
|
lCanvas := Canvas;
|
|
bmp := nil;
|
|
end else
|
|
begin
|
|
bmp := TBitmap.Create;
|
|
bmp.Width := 100;
|
|
bmp.Height := 100;
|
|
bmp.Canvas.Font.Assign(Font);
|
|
lCanvas := bmp.Canvas;
|
|
end;
|
|
|
|
VisCount := 0;
|
|
for x := 0 to FColumns.Count-1 do
|
|
begin
|
|
if FColumns[x].FVisible
|
|
then Inc(VisCount);
|
|
end;
|
|
|
|
if (VisCount = 0) then
|
|
begin
|
|
FixedHeight := 0;
|
|
FixedWidth := 0;
|
|
BodyWidth := 0;
|
|
BodyHeight := 0;
|
|
ShowHideScrollBar(False, False);
|
|
Exit;
|
|
end;
|
|
|
|
if FAutoColWidth then
|
|
begin
|
|
lCanvas.Font.Assign(Font);
|
|
for x := 0 to FColumns.Count-1
|
|
do FColumns[x].FWidth := Max(FDefColWidth, GetColAutoWidth(x));
|
|
end;
|
|
|
|
FixedWidth := 0;
|
|
if (FGutterKind <> gkNone)
|
|
then FixedWidth := FGutterWidth;
|
|
|
|
FixedHeight := FHeaderLine * FDefRowHeight;
|
|
BodyHeight := FRowCount * FDefRowHeight;
|
|
|
|
WidthAvail := ClientWidth - FixedWidth;
|
|
HeightAvail := ClientHeight - FixedHeight;
|
|
if FShowFooter
|
|
then HeightAvail := HeightAvail - FDefRowHeight;
|
|
|
|
BodyWidth := 0;
|
|
for x := 0 to FColumns.Count-1 do
|
|
begin
|
|
if FColumns[x].FVisible
|
|
then BodyWidth := BodyWidth + FColumns[x].FWidth;
|
|
end;
|
|
|
|
if FFitToWidth then
|
|
begin
|
|
if (BodyWidth < WidthAvail) then
|
|
begin
|
|
LastBodyWidth := BodyWidth;
|
|
x := 0;
|
|
while (BodyWidth < WidthAvail) do
|
|
begin
|
|
if (x > ColCount-1) then
|
|
begin
|
|
if (BodyWidth = LastBodyWidth)
|
|
then Break
|
|
else x := 0;
|
|
end;
|
|
if FColumns[x].FVisible and FColumns[x].FCanResize then
|
|
begin
|
|
FColumns[x].FWidth := FColumns[x].FWidth + 1;
|
|
Inc(BodyWidth);
|
|
end;
|
|
Inc(x);
|
|
end;
|
|
end;
|
|
if (BodyWidth > WidthAvail) then
|
|
begin
|
|
LastBodyWidth := BodyWidth;
|
|
x := 0;
|
|
while (BodyWidth > WidthAvail) do
|
|
begin
|
|
if (x > ColCount-1) then
|
|
begin
|
|
if (BodyWidth = LastBodyWidth)
|
|
then Break
|
|
else x := 0;
|
|
end;
|
|
if FColumns[x].FVisible and (x <> ForcedColumn) and FColumns[x].FCanResize then
|
|
begin
|
|
FColumns[x].FWidth := FColumns[x].FWidth - 1;
|
|
Dec(BodyWidth);
|
|
end;
|
|
Inc(x);
|
|
end;
|
|
end;
|
|
ForcedColumn := -1;
|
|
end;
|
|
|
|
if (BodyWidth < WidthAvail)
|
|
then FHorzOffset := 0;
|
|
|
|
if (BodyHeight < HeightAvail)
|
|
then FVertOffset := 0;
|
|
|
|
HVisible := BodyWidth > WidthAvail;
|
|
VVisible := BodyHeight > HeightAvail;
|
|
|
|
ShowHideScrollBar(HVisible, VVisible);
|
|
|
|
FMaxHScroll := Max(0, BodyWidth - ClientWidth + FixedWidth);
|
|
if FShowFooter
|
|
then FMaxVScroll := Max(0, BodyHeight - ClientHeight + FixedHeight + FDefRowHeight)
|
|
else FMaxVScroll := Max(0, BodyHeight - ClientHeight + FixedHeight);
|
|
|
|
// Align to FDefRowHeight
|
|
v := FMaxVScroll div FDefRowHeight;
|
|
if (FMaxVScroll mod FDefRowHeight) > 0
|
|
then Inc(v);
|
|
FMaxVScroll := v * FDefRowHeight;
|
|
|
|
if FShowFooter then
|
|
begin
|
|
if VVisible
|
|
then FooterTop := (((ClientHeight div FDefRowHeight) - 1) * FDefRowHeight) - 1
|
|
else FooterTop := (FDefRowHeight * (FHeaderLine + FRowCount)) - 1;
|
|
end;
|
|
|
|
FHorzOffset := Max(0, Min(FHorzOffset, FMaxHScroll));
|
|
FVertOffset := Max(0, Min(FVertOffset, FMaxVScroll));
|
|
|
|
SetScrollBar(SB_HORZ, FMaxHScroll, FHorzOffset, SIF_POS or SIF_RANGE);
|
|
SetScrollBar(SB_VERT, FMaxVScroll, FVertOffset, SIF_POS or SIF_RANGE);
|
|
|
|
AllWidth := Min(ClientWidth, BodyWidth + FixedWidth);
|
|
if FShowFooter then
|
|
begin
|
|
AllHeight := Min(ClientHeight, BodyHeight + FixedHeight + FDefRowHeight);
|
|
CellBox := Rect(FixedWidth, FixedHeight, ClientWidth, FooterTop);
|
|
end else
|
|
begin
|
|
AllHeight := Min(ClientHeight, BodyHeight + FixedHeight);
|
|
CellBox := Rect(FixedWidth, FixedHeight, ClientWidth, ClientHeight);
|
|
end;
|
|
|
|
bmp.Free;
|
|
end;
|
|
|
|
function DrawString(Canvas: TCanvas; Str: string; Rc: TRect;
|
|
HorzAlign: THorzAlign; VertAlign: TVertAlign; IsEditing: Boolean): TPoint;
|
|
var
|
|
w, h, x, y: Integer;
|
|
rw: Integer;
|
|
begin
|
|
w := Canvas.TextWidth(Str);
|
|
h := Canvas.TextHeight('gM');
|
|
x := 0;
|
|
y := 0;
|
|
rw := Rc.Right - rc.Left;
|
|
case HorzAlign of
|
|
haLeft:
|
|
begin
|
|
x := Rc.Left;
|
|
if (w > rw) and IsEditing
|
|
then x := Rc.Left - (w - rw);
|
|
end;
|
|
haCenter: x := Rc.Left + ((rw - w) div 2);
|
|
haRight: x := Rc.Right - w;
|
|
end;
|
|
case VertAlign of
|
|
vaTop: y := Rc.Top;
|
|
vaCenter: y := Rc.Top + (((Rc.Bottom - Rc.Top) - h) div 2);
|
|
vaBottom: y := Rc.Bottom - h;
|
|
end;
|
|
Canvas.TextRect(Rc, x, y, Str);
|
|
// Return next cursor position
|
|
Result := Point(Min(x + w + 1, Rc.Right), Rc.Top - 1);
|
|
end;
|
|
|
|
procedure DrawStringMulti(Canvas: TCanvas; Str: string; Rc: TRect;
|
|
HorzAlign: THorzAlign; VertAlign: TVertAlign);
|
|
var
|
|
w, h, x, y: Integer;
|
|
t: TStringList;
|
|
i: Integer;
|
|
dh: Integer;
|
|
|
|
begin
|
|
if Pos(';', Str) = 0 then
|
|
begin
|
|
DrawString(Canvas, Str, Rc, HorzAlign, VertAlign, False);
|
|
Exit;
|
|
end;
|
|
|
|
t := TStringList.Create;
|
|
t.Text := StringReplace(Str, ';', #13, [rfReplaceAll]);
|
|
h := Canvas.TextHeight('gM');
|
|
dh := Rc.Top + (((Rc.Bottom - Rc.Top) - (h * t.Count)) div 2);
|
|
for i := 0 to t.Count-1 do
|
|
begin
|
|
w := Canvas.TextWidth(t[i]);
|
|
x := 0;
|
|
y := 0;
|
|
case HorzAlign of
|
|
haLeft: x := Rc.Left;
|
|
haCenter: x := Rc.Left + (((Rc.Right - Rc.Left) - w) div 2);
|
|
haRight: x := Rc.Right - w;
|
|
end;
|
|
case VertAlign of
|
|
vaTop: y := Rc.Top + (i * h);
|
|
vaCenter: y := dh + (i * h);
|
|
vaBottom: y := Rc.Bottom - (h * (t.Count-i));
|
|
end;
|
|
Canvas.TextRect(Rc, x, y, t[i]);
|
|
end;
|
|
t.Free;
|
|
end;
|
|
|
|
function TNiceGrid.GetCellColor(X, Y: Integer): TColor;
|
|
var
|
|
cl: TColor;
|
|
R: TRect;
|
|
begin
|
|
cl := FColumns[x].Color;
|
|
if Odd(Y) then
|
|
begin
|
|
if (cl = FColor)
|
|
then cl := FAlternateColor;
|
|
end;
|
|
if FEnabled then
|
|
begin
|
|
with FSelectArea
|
|
do R := Rect(Left, Top, Right + 1, Bottom + 1);
|
|
if PtInRect(R, Point(X, Y)) then
|
|
begin
|
|
if not ((X = FCol) and (y = FRow))
|
|
then cl := FSelectionColor;
|
|
end;
|
|
end;
|
|
Result := cl;
|
|
end;
|
|
|
|
procedure TNiceGrid.DrawFixCell(Rc: TRect; Str: string; AFont: TFont; AEvent: TOnDrawHeaderEvent);
|
|
var
|
|
Rt: TRect;
|
|
Handled: Boolean;
|
|
begin
|
|
Handled := False;
|
|
with Canvas do
|
|
begin
|
|
// Clear area
|
|
if FFlat
|
|
then Pen.Color := FHeaderDarkColor
|
|
else Pen.Color := clBlack;
|
|
Brush.Style := bsSolid;
|
|
Brush.Color := FHeaderColor;
|
|
Font.Assign(AFont);
|
|
if not FEnabled
|
|
then Font.Color := FHeaderDarkColor;
|
|
if Assigned(AEvent)
|
|
then AEvent(Self, Canvas, Rc, Str, Handled);
|
|
if Handled
|
|
then Exit;
|
|
Rectangle(Rc);
|
|
// Draw text immediately
|
|
Brush.Style := bsClear;
|
|
Rt := Rect(Rc.Left + 2, Rc.Top + 2, Rc.Right - 3, Rc.Bottom - 3);
|
|
DrawStringMulti(Canvas, Str, Rt, haCenter, vaCenter);
|
|
// cosmetics
|
|
Pen.Color := FHeaderLightColor;
|
|
MoveTo(Rc.Left + 1, Rc.Bottom - 2);
|
|
LineTo(Rc.Left + 1, Rc.Top + 1);
|
|
LineTo(Rc.Right - 1, Rc.Top + 1);
|
|
if not FFlat then
|
|
begin
|
|
Pen.Color := FHeaderDarkColor;
|
|
MoveTo(Rc.Right - 2, Rc.Top + 1);
|
|
LineTo(Rc.Right - 2, Rc.Bottom - 2);
|
|
LineTo(Rc.Left, Rc.Bottom - 2);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.RenderGutter;
|
|
const
|
|
ArrowWidth = 8;
|
|
var
|
|
x: Integer;
|
|
R, Dummy: TRect;
|
|
Str: string;
|
|
l, t, m: Integer;
|
|
GutterBox: TRect;
|
|
begin
|
|
if (FGutterKind = gkNone)
|
|
then Exit;
|
|
GutterBox := CellBox;
|
|
GutterBox.Left := 0;
|
|
for x := 0 to FRowCount-1 do
|
|
begin
|
|
R := Rect(-1, (x * FDefRowHeight) - 1, FGutterWidth, ((x + 1) * FDefRowHeight));
|
|
OffsetRect(R, 0, -FVertOffset + FixedHeight);
|
|
Dummy := Rect(0, 0, 0, 0); // to silence the compiler
|
|
if IntersectRect(Dummy, R, GutterBox) then
|
|
begin
|
|
case FGutterKind of
|
|
gkBlank, gkPointer:
|
|
Str := '';
|
|
gkNumber:
|
|
Str := IntToStr(x + 1);
|
|
gkString:
|
|
if (x > FGutterStrings.Count-1)
|
|
then Str := ''
|
|
else Str := FGutterStrings[x];
|
|
end;
|
|
DrawFixCell(R, Str, FGutterFont, FOnDrawGutter);
|
|
// Draw pointer triangle
|
|
if (FGutterKind = gkpointer) and (x = FRow) then
|
|
begin
|
|
with Canvas do
|
|
begin
|
|
l := (FGutterWidth - ArrowWidth) div 2;
|
|
t := (FDefRowHeight - ArrowWidth) div 2;
|
|
m := R.Top + (FDefRowHeight div 2);
|
|
Pen.Color := FHeaderDarkColor;
|
|
MoveTo(l, R.Bottom - t);
|
|
LineTo(l, R.Top + t);
|
|
LineTo(l + ArrowWidth, m);
|
|
Pen.Color := FHeaderLightColor;
|
|
LineTo(l, R.Bottom - t);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TNicegrid.RenderHeader;
|
|
var
|
|
x: Integer;
|
|
R, Dummy: TRect;
|
|
P: PHeaderInfo;
|
|
begin
|
|
Canvas.Font.Assign(FHeaderFont);
|
|
for x := 0 to FHeaderInfos.Count-1 do
|
|
begin
|
|
P := PHeaderInfo(FHeaderInfos[x]);
|
|
R := Rect(
|
|
GetColCoord(P^.Rc.Left) - 1,
|
|
(FDefRowHeight * P^.Rc.Top) - 1,
|
|
GetColCoord(P^.Rc.Right + 1),
|
|
FDefRowHeight * (P^.Rc.Bottom + 1)
|
|
);
|
|
OffsetRect(R, -FHorzOffset + FixedWidth, 0);
|
|
Dummy := Rect(0, 0, 0, 0); // to silence the compiler
|
|
if IntersectRect(Dummy, R, ClientRect)
|
|
then DrawFixCell(R, P^.Str, FHeaderFont, FOnDrawHeader);
|
|
end;
|
|
R := Rect(-1, -1, FixedWidth, FixedHeight);
|
|
DrawFixCell(R, '', FHeaderFont, FOnDrawHeader);
|
|
end;
|
|
|
|
procedure TNiceGrid.RenderFooter;
|
|
var
|
|
x: Integer;
|
|
R, Dummy: TRect;
|
|
FooterBottom: Integer;
|
|
Right: Integer;
|
|
begin
|
|
Canvas.Font.Assign(FFooterFont);
|
|
FooterBottom := FooterTop + FDefRowHeight + 1;
|
|
for x := 0 to FColumns.Count-1 do
|
|
begin
|
|
R := Rect(GetColCoord(x)-1, FooterTop, GetColCoord(x+1), FooterBottom);
|
|
OffsetRect(R, -FHorzOffset + FixedWidth, 0);
|
|
Dummy := Rect(0, 0, 0, 0); // to silence the compiler
|
|
if IntersectRect(Dummy, R, ClientRect)
|
|
then DrawFixCell(R, FColumns[x].FFooter, FFooterFont, FOnDrawFooter);
|
|
end;
|
|
R := Rect(-1, FooterTop, FixedWidth, FooterBottom);
|
|
DrawFixCell(R, '', FFooterFont, FOnDrawFooter);
|
|
Right := Min(AllWidth, ClientWidth);
|
|
R := Rect(-1, FooterBottom-1, Right, ClientHeight);
|
|
DrawFixCell(R, '', FFooterFont, FOnDrawFooter);
|
|
end;
|
|
|
|
procedure TNiceGrid.DrawCell(X, Y: Integer);
|
|
var
|
|
Rc, Dummy: TRect;
|
|
Column: TNiceColumn;
|
|
Handled: Boolean;
|
|
begin
|
|
Handled := False;
|
|
Rc := GetCellRect(x, y);
|
|
OffsetRect(Rc, -FHorzOffset + FixedWidth, -FVertOffset + FixedHeight);
|
|
Dummy := Rect(0, 0, 0, 0); // to silence the compiler
|
|
if IntersectRect(Dummy, Rc, CellBox) then
|
|
begin
|
|
Column := FColumns[x];
|
|
with Canvas do
|
|
begin
|
|
Font.Assign(Column.Font);
|
|
if not FEnabled
|
|
then Font.Color := FGridColor;
|
|
Pen.Color := FGridColor;
|
|
Brush.Color := GetCellColor(X, Y);
|
|
|
|
if Assigned(FOnDrawCell)
|
|
then FOnDrawCell(Self, Canvas, X, Y, Rc, Handled);
|
|
|
|
if not Handled then
|
|
begin
|
|
Brush.Style := bsSolid;
|
|
if FShowGrid
|
|
then Rectangle(Rc)
|
|
else FillRect(Rc);
|
|
Brush.Style := bsClear;
|
|
InflateRect(Rc, -4, -2);
|
|
DrawString(Canvas, SafeGetCell(x, y), Rc, Column.HorzAlign,
|
|
Column.VertAlign, False);
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.InvalidateCell(X, Y: Integer);
|
|
var
|
|
Rc: TRect;
|
|
begin
|
|
Rc := GetCellRect(X, Y);
|
|
InvalidateRect(Handle, @Rc, false);
|
|
end;
|
|
|
|
function TNiceGrid.FastDrawCell(X, Y: Integer; IsEditing: Boolean): TPoint;
|
|
var
|
|
R, Dummy: TRect;
|
|
Handled: Boolean;
|
|
Column: TNiceColumn;
|
|
begin
|
|
Handled := False;
|
|
Result := Point(-1, -1);
|
|
R := GetCellRect(x, y);
|
|
OffsetRect(R, -FHorzOffset + FixedWidth, -FVertOffset + FixedHeight);
|
|
Dummy := Rect(0, 0, 0, 0); // to silence the compiler
|
|
if IntersectRect(Dummy, R, CellBox) then
|
|
begin
|
|
Column := FColumns[x];
|
|
with Canvas do
|
|
begin
|
|
Brush.Color := GetCellColor(X, Y);
|
|
Font.Assign(Column.Font);
|
|
end;
|
|
if Assigned(FOnDrawCell)
|
|
then FOnDrawCell(Self, Canvas, X, Y, R, Handled);
|
|
if not Handled then
|
|
begin
|
|
with Canvas do
|
|
begin
|
|
Brush.Style := bsSolid;
|
|
InflateRect(R, -4, -2);
|
|
FillRect(R);
|
|
Brush.Style := bsClear;
|
|
end;
|
|
Result := DrawString(Canvas, SafeGetCell(x, y), R, Column.HorzAlign,
|
|
Column.VertAlign, IsEditing);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.DrawSelection;
|
|
var
|
|
R, R1, R2: TRect;
|
|
HOffset, VOffset: Integer;
|
|
boxSize: Integer;
|
|
boxSize2: Integer;
|
|
|
|
begin
|
|
|
|
if (FCol = -1) or (FRow = -1)
|
|
then Exit;
|
|
|
|
HOffset := - FHorzOffset + FixedWidth;
|
|
VOffset := - FVertOffset + FixedHeight;
|
|
|
|
R1 := GetCellRect(FSelectArea.Left, FSelectArea.Top);
|
|
R2 := GetCellRect(FSelectArea.Right, FSelectArea.Bottom);
|
|
R := Rect(R1.Left, R1.Top, R2.Right, R2.Bottom);
|
|
OffsetRect(R, HOffset, VOffset);
|
|
|
|
with Canvas do
|
|
begin
|
|
|
|
if Focused
|
|
then Pen.Color := clBlack
|
|
else Pen.Color := FGridColor;
|
|
|
|
Pen.Width := 3;
|
|
Brush.Style := bsClear;
|
|
Rectangle(R);
|
|
|
|
Pen.Width := 1;
|
|
Brush.Style := bsSolid;
|
|
if Focused
|
|
then Brush.Color := clBlack
|
|
else Brush.Color := FGridColor;
|
|
Pen.Color := clWhite;
|
|
|
|
boxSize2 := 6;
|
|
{$IFDEF FPC}
|
|
boxSize2 := Scale96ToFont(boxSize2);
|
|
{$ENDIF}
|
|
boxSize := boxSize2 div 2;
|
|
|
|
case SmallBoxPos of
|
|
0: SmallBox := Rect(R.Right, R.Bottom, R.Right, R.Bottom);
|
|
1: SmallBox := Rect(R.Right, R.Top + boxSize2-1, R.Right, R.Top + boxSize2-1);
|
|
2: SmallBox := Rect(R.Left + boxSize2-1, R.Bottom, R.Left + boxSize2-1, R.Bottom);
|
|
end;
|
|
InflateRect(SmallBox, boxSize, boxSize);
|
|
{
|
|
case SmallBoxPos of
|
|
0: SmallBox := Rect(R.Right - 3, R.Bottom - 3, R.Right + 3, R.Bottom + 3);
|
|
1: SmallBox := Rect(R.Right - 3, R.Top - 3 + 5, R.Right + 3, R.Top + 3 + 5);
|
|
2: SmallBox := Rect(R.Left - 3 + 5, R.Bottom - 3, R.Left + 3 + 5, R.Bottom + 3);
|
|
end;
|
|
}
|
|
|
|
Rectangle(SmallBox);
|
|
SmallBoxPos := 0; // Reset to Right Bottom
|
|
|
|
end;
|
|
|
|
if (SmallBoxArea.Left <> -1) then
|
|
begin
|
|
R1 := GetCellRect(SmallBoxArea.Left, SmallBoxArea.Top);
|
|
OffsetRect(R1, HOffset, VOffset);
|
|
R2 := GetCellRect(SmallBoxArea.Right, SmallBoxArea.Bottom);
|
|
OffsetRect(R2, HOffset, VOffset);
|
|
R := Rect(R1.Left, R1.Top, R2.Right, R2.Bottom);
|
|
|
|
with Canvas do
|
|
begin
|
|
Pen.Color := clBlack;
|
|
Pen.Width := 1;
|
|
Pen.Style := psDot;
|
|
Brush.Style := bsClear;
|
|
Rectangle(R);
|
|
Pen.Style := psSolid;
|
|
Pen.Width := 1;
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TNiceGrid.ClearUnused;
|
|
var
|
|
t: Integer;
|
|
begin
|
|
if (AllWidth < ClientWidth) then
|
|
begin
|
|
with Canvas do
|
|
begin
|
|
Brush.Style := bsSolid;
|
|
Brush.Color := FColor;
|
|
FillRect(Rect(AllWidth, 0, ClientWidth, ClientHeight));
|
|
end;
|
|
end;
|
|
if FShowFooter
|
|
then Exit;
|
|
if (AllHeight < ClientHeight) then
|
|
begin
|
|
with Canvas do
|
|
begin
|
|
Brush.Style := bsSolid;
|
|
Brush.Color := FColor;
|
|
FillRect(Rect(0, AllHeight, ClientWidth, ClientHeight));
|
|
end;
|
|
end;
|
|
if ((FMaxVScroll - FVertOffset) < FDefRowHeight) then
|
|
begin
|
|
with Canvas do
|
|
begin
|
|
Brush.Style := bsSolid;
|
|
Brush.Color := FColor;
|
|
t := FixedHeight + (((ClientHeight - FixedHeight) div FDefRowHeight) * FDefRowHeight);
|
|
FillRect(Rect(0, t, ClientWidth, ClientHeight));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.Paint;
|
|
var
|
|
x, y: Integer;
|
|
RgnInv, RgnAll, RgnBody, RgnSel, Temp: HRGN;
|
|
HOffset, VOffset: Integer;
|
|
R, R1, R2: TRect;
|
|
|
|
begin
|
|
|
|
if FUpdating then Exit;
|
|
if not HandleAllocated then Exit;
|
|
|
|
if (ColCount = 0) then
|
|
begin
|
|
with Canvas do
|
|
begin
|
|
Brush.Style := bsSolid;
|
|
Brush.Color := FColor;
|
|
FillRect(Rect(0, 0, ClientWidth, ClientHeight));
|
|
end;
|
|
Exit;
|
|
end;
|
|
|
|
if (FRowCount > 0) then
|
|
begin
|
|
|
|
// Calculating area that will be covered by selection rectangle
|
|
HOffset := - FHorzOffset + FixedWidth;
|
|
VOffset := - FVertOffset + FixedHeight;
|
|
R1 := GetCellRect(FSelectArea.Left, FSelectArea.Top);
|
|
R2 := GetCellRect(FSelectArea.Right, FSelectArea.Bottom);
|
|
R := Rect(R1.Left, R1.Top, R2.Right, R2.Bottom);
|
|
OffsetRect(R, HOffset, VOffset);
|
|
|
|
// Creating region, excluding selection rectangle to reduce flicker
|
|
RgnSel := CreateRectRgn(R.Left-1, R.Top-1, R.Right+1, R.Bottom+1);
|
|
Temp := CreateRectRgn(R.Left+2, R.Top+2, R.Right-2, R.Bottom-2);
|
|
CombineRgn(RgnSel, RgnSel, Temp, RGN_XOR);
|
|
|
|
if FShowFooter
|
|
then RgnInv := CreateRectRgn(FixedWidth, FixedHeight, ClientWidth, FooterTop)
|
|
else RgnInv := CreateRectRgn(FixedWidth, FixedHeight, ClientWidth, ClientHeight);
|
|
if FEnabled
|
|
then CombineRgn(RgnInv, RgnInv, RgnSel, RGN_DIFF);
|
|
SelectClipRgn(Canvas.Handle, RgnInv);
|
|
|
|
for x := 0 to ColCount-1 do
|
|
begin
|
|
if FColumns[x].FVisible then
|
|
begin
|
|
for y := 0 to FRowCount-1 do
|
|
begin
|
|
if (Integer(GetObject(x, y)) <> MergeID)
|
|
then DrawCell(X, Y);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
for x := 0 to Mergeds.Count-1
|
|
do DrawMergedCell(x);
|
|
|
|
RgnAll := CreateRectRgn(0, 0, ClientWidth, ClientHeight);
|
|
if FEnabled
|
|
then CombineRgn(RgnAll, RgnAll, RgnSel, RGN_DIFF);
|
|
SelectClipRgn(Canvas.Handle, RgnAll);
|
|
ClearUnused;
|
|
|
|
if FShowFooter
|
|
then RgnBody := CreateRectRgn(FixedWidth, FixedHeight, ClientWidth, FooterTop)
|
|
else RgnBody := CreateRectRgn(FixedWidth, FixedHeight, ClientWidth, ClientHeight);
|
|
SelectClipRgn(Canvas.Handle, RgnBody);
|
|
if FEnabled
|
|
then DrawSelection;
|
|
|
|
SelectClipRgn(Canvas.Handle, 0);
|
|
|
|
DeleteObject(RgnInv);
|
|
DeleteObject(RgnAll);
|
|
DeleteObject(RgnBody);
|
|
DeleteObject(RgnSel);
|
|
DeleteObject(Temp);
|
|
|
|
end else
|
|
|
|
ClearUnused;
|
|
|
|
RenderGutter;
|
|
RenderHeader;
|
|
if FShowFooter
|
|
then RenderFooter;
|
|
|
|
end;
|
|
|
|
procedure TNiceGrid.UpdateHeader;
|
|
var
|
|
P: PHeaderInfo;
|
|
x, y: Integer;
|
|
t: TStringList;
|
|
s: string;
|
|
LastX: TList;
|
|
LastY: PHeaderInfo;
|
|
Blank: PHeaderInfo;
|
|
|
|
begin
|
|
|
|
ClearHeaderInfos;
|
|
|
|
LastX := TList.Create;
|
|
t := TStringList.Create;
|
|
|
|
Blank := New(PHeaderInfo);
|
|
Blank^.Str := '^%%%%%^******^';
|
|
|
|
while (LastX.Count < FHeaderLine)
|
|
do LastX.Add(Blank);
|
|
|
|
P := nil;
|
|
for x := 0 to FColumns.Count-1 do
|
|
begin
|
|
if not FColumns[x].FVisible then
|
|
begin
|
|
for y := 0 to FHeaderLine-1
|
|
do LastX[y] := Blank;
|
|
Continue;
|
|
end;
|
|
t.Text := StringReplace(FColumns[x].Title, '|', #13, [rfReplaceAll]);
|
|
while (t.Count < FHeaderLine) do
|
|
begin
|
|
if (t.Count = 0)
|
|
then t.Add('')
|
|
else t.Add( t[t.Count-1]);
|
|
end;
|
|
LastY := Blank;
|
|
for y := 0 to FHeaderLine-1 do
|
|
begin
|
|
s := t[y];
|
|
if (s = LastY.Str) then
|
|
begin
|
|
LastY^.Rc.Bottom := Min(FHeaderLine-1, Max(LastY^.Rc.Bottom, y));
|
|
end else
|
|
begin
|
|
if (s = PHeaderInfo(LastX[y])^.Str) then
|
|
begin
|
|
P := PHeaderInfo(LastX[y]);
|
|
P^.Rc.Right := P^.Rc.Right + 1;
|
|
end else
|
|
begin
|
|
P := New(PHeaderInfo);
|
|
P^.Rc := Rect(x, y, x, y);
|
|
P^.Str := s;
|
|
FHeaderInfos.Add(P);
|
|
end;
|
|
LastX[y] := P;
|
|
end;
|
|
LastY := P;
|
|
end;
|
|
end;
|
|
|
|
LastX.Free;
|
|
t.Free;
|
|
Dispose(Blank);
|
|
|
|
Recalculate;
|
|
end;
|
|
|
|
function TNiceGrid.GetColCoord(I: Integer): Integer;
|
|
var
|
|
x: Integer;
|
|
Column: TNiceColumn;
|
|
begin
|
|
Result := 0;
|
|
for x := 0 to I-1 do
|
|
begin
|
|
Column := FColumns[x];
|
|
if Column.FVisible
|
|
then Result := Result + Column.FWidth;
|
|
end;
|
|
end;
|
|
|
|
function TNiceGrid.GetCellRect(x, y: Integer): TRect;
|
|
var
|
|
l, t, w, h: Integer;
|
|
begin
|
|
if (x = -1) or (y = -1) then
|
|
begin
|
|
Result := Rect(0, 0, 0, 0);
|
|
Exit;
|
|
end;
|
|
l := GetColCoord(x);
|
|
t := FDefRowheight * y;
|
|
w := 0;
|
|
if (FColumns[x].FVisible)
|
|
then w := FColumns[x].FWidth;
|
|
h := FDefRowHeight;
|
|
Result := Rect(l-1, t-1, l + w, t + h);
|
|
end;
|
|
|
|
function TNiceGrid.CellRectToClient(R: TRect): TRect;
|
|
begin
|
|
Result := R;
|
|
OffsetRect(Result, - FHorzOffset + FixedWidth, - FVertOffset + FixedHeight);
|
|
end;
|
|
|
|
function TNiceGrid.GetCellAtPos(X, Y: Integer): TPoint;
|
|
var
|
|
ax, ay: Integer;
|
|
begin
|
|
ax := (FHorzOffset + X) - FixedWidth;
|
|
ay := (FVertOffset + Y) - FixedHeight;
|
|
Result.X := 0;
|
|
while (GetColCoord(Result.X) < ax) do
|
|
begin
|
|
Result.X := Result.X + 1;
|
|
if (Result.X > FColumns.Count-1)
|
|
then Break;
|
|
end;
|
|
Result.X := Max(0, Result.X - 1);
|
|
Result.Y := Max(0, Min(ay div FDefRowHeight, FRowCount-1));
|
|
end;
|
|
|
|
function TNiceGrid.GetColFromX(X: Integer): Integer;
|
|
var
|
|
ax: Integer;
|
|
begin
|
|
if (X < FixedWidth) then
|
|
begin
|
|
Result := -1;
|
|
Exit;
|
|
end;
|
|
Result := 0;
|
|
ax := (FHorzOffset + X) - FixedWidth;
|
|
while (GetColCoord(Result) < ax) do
|
|
begin
|
|
Result := Result + 1;
|
|
if (Result > FColumns.Count-1)
|
|
then Break;
|
|
end;
|
|
Result := Result - 1;
|
|
if (Result > FColumns.Count-1) or (Result < 0)
|
|
then Result := -1;
|
|
end;
|
|
|
|
function TNiceGrid.GetRowFromY(Y: Integer): Integer;
|
|
var
|
|
ay: Integer;
|
|
begin
|
|
if (Y < FixedHeight) then
|
|
begin
|
|
Result := -1;
|
|
Exit;
|
|
end;
|
|
ay := (FVertOffset + Y) - FixedHeight;
|
|
Result := ay div FDefRowHeight;
|
|
if (Result > FRowCount-1)
|
|
then Result := -1;
|
|
end;
|
|
|
|
function TNiceGrid.SafeGetCell(X, Y: Integer): string;
|
|
var
|
|
t: TStringList;
|
|
begin
|
|
Result := '';
|
|
t := TStringList(Columns[X].FStrings);
|
|
if (Y < t.Count)
|
|
then Result := t[Y];
|
|
end;
|
|
|
|
function TNiceGrid.GetCell(X, Y: Integer): string;
|
|
var
|
|
t: TStrings;
|
|
begin
|
|
Result := '';
|
|
if (X > ColCount-1) or (Y > FRowCount-1)
|
|
then raise Exception.Create('Cell Index out of bound.');
|
|
t := Columns[X].FStrings;
|
|
if (Y < t.Count)
|
|
then Result := t[Y];
|
|
end;
|
|
|
|
procedure TNiceGrid.InternalSetCell(X, Y: Integer; Value: string;
|
|
FireOnChange: Boolean);
|
|
var
|
|
t: TStringList;
|
|
s: string;
|
|
CanChange: Boolean;
|
|
begin
|
|
if (ColCount = 0) or (FRowCount = 0)
|
|
then Exit;
|
|
if FireOnChange and FColumns[X].FReadOnly
|
|
then Exit;
|
|
if (X > ColCount-1) or (Y > FRowCount-1)
|
|
then raise Exception.Create('Cell Index out of bound.');
|
|
t := TStringList(FColumns[X].FStrings);
|
|
while (Y > t.Count-1)
|
|
do t.Add('');
|
|
if (t[Y] = Value)
|
|
then Exit;
|
|
if FireOnChange then
|
|
begin
|
|
s := Value;
|
|
CanChange := True;
|
|
if Assigned(FOnCellChanging)
|
|
then FOnCellChanging(Self, X, Y, CanChange);
|
|
if not CanChange
|
|
then Exit;
|
|
if Assigned(FOnCellChange)
|
|
then FOnCellChange(Self, X, Y, s);
|
|
t[Y] := s;
|
|
end else
|
|
t[Y] := Value;
|
|
if not FUpdating
|
|
then InvalidateCell(X, Y);
|
|
// then FastDrawCell(X, Y, False);
|
|
end;
|
|
|
|
procedure TNiceGrid.SetCell(X, Y: Integer; Value: string);
|
|
begin
|
|
InternalSetCell(X, Y, Value, False);
|
|
end;
|
|
|
|
procedure TNiceGrid.BeginUpdate;
|
|
begin
|
|
FUpdating := True;
|
|
ForceHideCaret;
|
|
end;
|
|
|
|
procedure TNiceGrid.EndUpdate;
|
|
begin
|
|
FUpdating := False;
|
|
UpdateHeader;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetFlat(Value: Boolean);
|
|
begin
|
|
if (FFlat <> Value) then
|
|
begin
|
|
FFlat := Value;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetColor(Value: TColor);
|
|
begin
|
|
if (FColor <> Value) then
|
|
begin
|
|
FColor := Value;
|
|
inherited Color := Value;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetAlternateColor(Value: TColor);
|
|
begin
|
|
if (FAlternateColor <> Value) then
|
|
begin
|
|
FAlternateColor := Value;
|
|
InvalidateCells;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetGridColor(Value: TColor);
|
|
begin
|
|
if (FGridColor <> Value) then
|
|
begin
|
|
FGridColor := Value;
|
|
InvalidateCells;
|
|
end;
|
|
end;
|
|
|
|
function TNiceGrid.GetColWidths(Index: Integer): Integer;
|
|
begin
|
|
Result := FColumns[Index].FWidth;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetColWidths(Index, Value: Integer);
|
|
begin
|
|
if not FAutoColWidth then
|
|
begin
|
|
if (ColWidths[Index] <> Value)
|
|
then FColumns[Index].Width := Value;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetAutoColWidth(Value: Boolean);
|
|
begin
|
|
if (FAutoColWidth <> Value) then
|
|
begin
|
|
FAutoColWidth := Value;
|
|
Recalculate;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetDefColWidth(Value: Integer);
|
|
begin
|
|
if (FDefColWidth <> Value) then
|
|
begin
|
|
FDefColWidth := Value;
|
|
if not FAutoColWidth then
|
|
begin
|
|
Recalculate;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetDefRowHeight(Value: Integer);
|
|
begin
|
|
if (FDefRowHeight <> Value) then
|
|
begin
|
|
FDefRowHeight := Value;
|
|
FSmallChange := Value;
|
|
FLargeChange := Value * 5;
|
|
Recalculate;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetFitToWidth(Value: Boolean);
|
|
begin
|
|
if (FFitToWidth <> Value) then
|
|
begin
|
|
FFitToWidth := Value;
|
|
FHorzOffset := 0;
|
|
Recalculate;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetHeaderColor(Value: TColor);
|
|
begin
|
|
if (FHeaderColor <> Value) then
|
|
begin
|
|
FHeaderColor := Value;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetHeaderDarkColor(Value: TColor);
|
|
begin
|
|
if (FHeaderDarkColor <> Value) then
|
|
begin
|
|
FHeaderDarkColor := Value;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetHeaderLightColor(Value: TColor);
|
|
begin
|
|
if (FHeaderLightColor <> Value) then
|
|
begin
|
|
FHeaderLightColor := Value;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetHeaderLine(Value: Integer);
|
|
begin
|
|
if (FHeaderLine <> Value) then
|
|
begin
|
|
FHeaderLine := Value;
|
|
UpdateHeader;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetSelectionColor(Value: TColor);
|
|
begin
|
|
if (FSelectionColor <> Value) then
|
|
begin
|
|
FSelectionColor := Value;
|
|
InvalidateCells;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.KeyDown(var Key: Word; Shift: TShiftState);
|
|
var
|
|
l, t, r, b: Integer;
|
|
x, y: Integer;
|
|
Empty: Boolean;
|
|
Str: string;
|
|
FillDown: Boolean;
|
|
FillRight: Boolean;
|
|
Old: Integer;
|
|
OldS: string;
|
|
|
|
procedure UpdateColRow;
|
|
begin
|
|
ForceHideCaret;
|
|
FUpdating := True;
|
|
BuffString := '';
|
|
FCol2 := FCol;
|
|
FRow2 := FRow;
|
|
EnsureVisible(FCol, FRow);
|
|
FUpdating := False;
|
|
SetSelectArea(Rect(FCol, FRow, FCol, FRow));
|
|
ColRowChanged;
|
|
end;
|
|
|
|
procedure UpdateSelectArea;
|
|
begin
|
|
l := Min(FCol2, FCol);
|
|
t := Min(FRow2, FRow);
|
|
r := Max(FCol2, FCol);
|
|
b := Max(FRow2, FRow);
|
|
SetSelectArea(Rect(l, t, r, b));
|
|
EnsureVisible(FCol2, FRow2);
|
|
end;
|
|
|
|
begin
|
|
|
|
if not FEnabled
|
|
then Exit;
|
|
|
|
if (ColCount = 0) or (FRowCount = 0)
|
|
then Exit;
|
|
|
|
if (ssCtrl in Shift) then
|
|
begin
|
|
|
|
case Key of
|
|
|
|
Ord('X'), Ord('x'):
|
|
if not FReadOnly
|
|
then CutToClipboard;
|
|
|
|
Ord('C'), Ord('c'):
|
|
CopyToClipboard;
|
|
|
|
Ord('V'), Ord('v'):
|
|
if not FReadOnly
|
|
then PasteFromClipboard;
|
|
|
|
VK_HOME:
|
|
begin
|
|
FCol := GetFirstVisible;
|
|
FRow := 0;
|
|
UpdateColRow;
|
|
end;
|
|
|
|
VK_END:
|
|
begin
|
|
FCol := GetLastVisible;
|
|
FRow := FRowCount-1;
|
|
UpdateColRow;
|
|
end;
|
|
|
|
VK_DELETE:
|
|
begin
|
|
if not FReadOnly and (FRowCount > 1) then
|
|
begin
|
|
Old := FRow;
|
|
DeleteRow(FRow);
|
|
if Assigned(FOnDeleteRow)
|
|
then FOnDeleteRow(Self, Old);
|
|
UpdateColRow;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
end else
|
|
|
|
if (ssShift in Shift) then
|
|
begin
|
|
|
|
case Key of
|
|
|
|
VK_LEFT:
|
|
begin
|
|
FCol2 := Max(GetPrevVisible(FCol2), GetFirstVisible);
|
|
UpdateSelectArea;
|
|
end;
|
|
|
|
VK_RIGHT:
|
|
begin
|
|
FCol2 := Min(GetNextVisible(FCol2), GetLastVisible);
|
|
UpdateSelectArea;
|
|
end;
|
|
|
|
VK_UP:
|
|
begin
|
|
FRow2 := Max(FRow2 - 1, 0);
|
|
UpdateSelectArea;
|
|
end;
|
|
|
|
VK_DOWN:
|
|
begin
|
|
FRow2 := Min(FRow2 + 1, FRowCount-1);
|
|
UpdateSelectArea;
|
|
end;
|
|
|
|
VK_RETURN:
|
|
if (FSelectArea.Left = FSelectArea.Right)
|
|
and (FSelectArea.Top = FSelectArea.Bottom) then
|
|
begin
|
|
FRow := Max(0, FRow - 1);
|
|
UpdateColRow;
|
|
end else
|
|
begin
|
|
if (FCol = FSelectArea.Left) and (FRow = FSelectArea.Top) then
|
|
begin
|
|
FCol := FSelectArea.Right;
|
|
FRow := FSelectArea.Bottom;
|
|
end else
|
|
if (FRow = FSelectArea.Top) then
|
|
begin
|
|
FCol := FCol - 1;
|
|
FRow := FSelectArea.Bottom;
|
|
end else
|
|
begin
|
|
FRow := Row - 1;
|
|
end;
|
|
ForceHideCaret;
|
|
BuffString := '';
|
|
EnsureVisible(FCol, FRow);
|
|
InvalidateCells;
|
|
ColRowChanged;
|
|
end;
|
|
|
|
end;
|
|
|
|
end else
|
|
begin
|
|
|
|
case Key of
|
|
|
|
VK_HOME:
|
|
begin
|
|
FCol := GetFirstVisible;
|
|
UpdateColRow;
|
|
end;
|
|
|
|
VK_END:
|
|
begin
|
|
FCol := GetLastVisible;
|
|
UpdateColRow;
|
|
end;
|
|
|
|
VK_PRIOR:
|
|
begin
|
|
FRow := 0;
|
|
UpdateColRow;
|
|
end;
|
|
|
|
VK_NEXT:
|
|
begin
|
|
FRow := FRowCount-1;
|
|
UpdateColRow;
|
|
end;
|
|
|
|
VK_LEFT:
|
|
begin
|
|
FCol := Max(GetPrevVisible(FCol), GetFirstVisible);
|
|
UpdateColRow;
|
|
end;
|
|
|
|
VK_RIGHT:
|
|
begin
|
|
FCol := Min(GetNextVisible(FCol), GetLastVisible);
|
|
UpdateColRow;
|
|
end;
|
|
|
|
VK_UP:
|
|
begin
|
|
if FAutoAddRow and (FRow = (FRowCount-1)) and (FRow > 0) and not FReadOnly then
|
|
begin
|
|
Empty := True;
|
|
for x := 0 to ColCount-1 do
|
|
begin
|
|
if (SafeGetCell(x, FRowCount-1) <> '') then
|
|
begin
|
|
Empty := False;
|
|
Break;
|
|
end;
|
|
end;
|
|
if Empty then
|
|
begin
|
|
RowCount := RowCount - 1;
|
|
FRow := FRowCount - 1;
|
|
if Assigned(FOnDeleteRow)
|
|
then FOnDeleteRow(Self, FRowCount);
|
|
end else
|
|
FRow := Max(0, FRow - 1);
|
|
end else
|
|
FRow := Max(0, FRow - 1);
|
|
UpdateColRow;
|
|
end;
|
|
|
|
VK_DOWN:
|
|
begin
|
|
if FAutoAddRow and (FRow = (FRowCount-1)) and not FReadOnly then
|
|
begin
|
|
Inc(FRow);
|
|
RowCount := RowCount + 1;
|
|
if Assigned(FOnInsertRow)
|
|
then FOnInsertRow(Self, FRow);
|
|
end else
|
|
FRow := Min(FRowCount - 1, FRow + 1);
|
|
UpdateColRow;
|
|
end;
|
|
|
|
{$IFDEF FPC}
|
|
VK_F2:
|
|
begin
|
|
{
|
|
BuffString := '';
|
|
Pt := GetCellAtPos(X, Y);
|
|
FCol := Pt.X;
|
|
FRow := Pt.Y;
|
|
// if (Pt.X = FCol) and (Pt.Y = FRow) then
|
|
begin
|
|
}
|
|
EnsureVisible(FCol, FRow);
|
|
if (not FReadOnly) and (not FColumns[FCol].FReadOnly) then
|
|
begin
|
|
IsEditing := True;
|
|
FEdit.ShowEdit(FCol, FRow);
|
|
FEdit.SelectAll;
|
|
end;
|
|
//end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
VK_RETURN:
|
|
begin
|
|
OldS := GetCell(Col, Row);
|
|
Str := OldS;
|
|
|
|
if Assigned(FOnCellAssignment)
|
|
then FOnCellAssignment(Self, Col, Row, Str);
|
|
|
|
if (Str <> Olds)
|
|
then InternalSetCell(Col, Row, Str, True);
|
|
|
|
FillDown := FAutoFillDown and (Copy(Str, Length(Str), 1) = '*');
|
|
FillRight := FAutoFillRight and (Copy(Str, Length(Str), 1) = '>');
|
|
|
|
if (FSelectArea.Left = FSelectArea.Right)
|
|
and (FSelectArea.Top = FSelectArea.Bottom) then
|
|
begin
|
|
if FillDown then
|
|
begin
|
|
BuffString := '';
|
|
ForceHideCaret;
|
|
Str := Copy(Str, 1, Length(Str) - 1);
|
|
for y := Row to FRowCount-1
|
|
do Cells[Col, y] := Str;
|
|
end else
|
|
if FillRight then
|
|
begin
|
|
BuffString := '';
|
|
ForceHideCaret;
|
|
Str := Copy(Str, 1, Length(Str) - 1);
|
|
for x := Col to ColCount-1
|
|
do Cells[x, Row] := Str;
|
|
end else
|
|
begin
|
|
FRow := Min(FRowCount - 1, FRow + 1);
|
|
UpdateColRow;
|
|
end;
|
|
end else
|
|
begin
|
|
if FillDown then
|
|
begin
|
|
BuffString := '';
|
|
ForceHideCaret;
|
|
Str := Copy(Str, 1, Length(Str) - 1);
|
|
for y := Row to FSelectArea.Bottom
|
|
do Cells[Col, y] := Str;
|
|
end else
|
|
if FillRight then
|
|
begin
|
|
BuffString := '';
|
|
ForceHideCaret;
|
|
Str := Copy(Str, 1, Length(Str) - 1);
|
|
for x := Col to FSelectArea.Right
|
|
do Cells[x, Row] := Str;
|
|
end else
|
|
begin
|
|
if (FCol = FSelectArea.Right) and (FRow = FSelectArea.Bottom) then
|
|
begin
|
|
FCol := FSelectArea.Left;
|
|
FRow := FSelectArea.Top;
|
|
end else
|
|
if (FRow = FSelectArea.Bottom) then
|
|
begin
|
|
FCol := FCol + 1;
|
|
FRow := FSelectArea.Top;
|
|
end else
|
|
begin
|
|
FRow := Row + 1;
|
|
end;
|
|
ForceHideCaret;
|
|
BuffString := '';
|
|
EnsureVisible(FCol, FRow);
|
|
InvalidateCells;
|
|
ColRowChanged;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
VK_DELETE:
|
|
begin
|
|
if (BuffString = '') then
|
|
begin
|
|
if not FReadOnly then
|
|
begin
|
|
FUpdating := True;
|
|
for x := SelectArea.Left to SelectArea.Right do
|
|
begin
|
|
for y := SelectArea.Top to SelectArea.Bottom
|
|
do InternalSetCell(X, Y, '', True);
|
|
end;
|
|
FUpdating := False;
|
|
InvalidateCells;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
VK_INSERT:
|
|
begin
|
|
if not FReadOnly then
|
|
begin
|
|
InsertRow(Max(0, FRow));
|
|
if Assigned(FOnInsertRow)
|
|
then FOnInsertRow(Self, FRow);
|
|
UpdateColRow;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
procedure TNiceGrid.KeyPress(var Key: Char);
|
|
var
|
|
Pt: TPoint;
|
|
Allowed: Boolean;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
if not FEnabled
|
|
then Exit;
|
|
|
|
if (ColCount = 0) or (FRowCount = 0)
|
|
then Exit;
|
|
|
|
if not FReadOnly then
|
|
begin
|
|
|
|
case Key of
|
|
|
|
Chr(VK_BACK):
|
|
begin
|
|
ForceHideCaret;
|
|
BuffString := Copy(BuffString, 1, Length(BuffString) - 1);
|
|
InternalSetCell(FCol, FRow, BuffString, True);
|
|
EnsureVisible(FCol, FRow);
|
|
Pt := FastDrawCell(FCol, FRow, True);
|
|
SetCaretPos(Pt.X, Pt.Y);
|
|
ForceShowCaret;
|
|
end;
|
|
|
|
Chr($20)..Chr($FF):
|
|
begin
|
|
Allowed := True;
|
|
if Assigned(FOnFilterChar)
|
|
then FOnFilterChar(Self, FCol, FRow, Key, Allowed);
|
|
if Allowed then
|
|
begin
|
|
ForceHideCaret;
|
|
BuffString := BuffString + Key;
|
|
InternalSetCell(FCol, FRow, BuffString, True);
|
|
EnsureVisible(FCol, FRow);
|
|
Pt := FastDrawCell(FCol, FRow, True);
|
|
SetCaretPos(Pt.X, Pt.Y);
|
|
ForceShowCaret;
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
function TNiceGrid.TextExtent(const s: String): TSize;
|
|
var
|
|
bmp: TBitmap;
|
|
begin
|
|
if Canvas.HandleAllocated then
|
|
Result := Canvas.TextExtent(s)
|
|
else
|
|
begin
|
|
bmp := TBitmap.Create;
|
|
try
|
|
bmp.Width := 100;
|
|
bmp.Height := 100;
|
|
bmp.Canvas.Font.Assign(self.Font);
|
|
Result := bmp.Canvas.TextExtent(s);
|
|
finally
|
|
bmp.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TNiceGrid.GetHitTestInfo(X, Y: Integer): TGridHitTest;
|
|
var
|
|
a, i1, i2: Integer;
|
|
ax, ay: Integer;
|
|
IsSizing: Boolean;
|
|
|
|
begin
|
|
Result := gtNone;
|
|
IsSizing := False;
|
|
|
|
ax := (FHorzOffset + X) - FixedWidth;
|
|
ay := (FVertOffset + Y) - FixedHeight;
|
|
|
|
if not FAutoColWidth then
|
|
begin
|
|
for a := 1 to ColCount do
|
|
begin
|
|
i1 := GetColCoord(a);
|
|
i2 := X + FHorzOffset - FixedWidth;
|
|
if (i2 > (i1-2)) and (i2 < (i1+2)) then
|
|
begin
|
|
SizingCol := a - 1;
|
|
IsSizing := FColumns[SizingCol].FCanResize;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if PtInRect(SmallBox, Point(X, Y))
|
|
then Result := gtSmallBox else
|
|
if IsSizing
|
|
then Result := gtColSizing else
|
|
if ((X < FixedWidth) and (Y < FixedHeight))
|
|
then Result := gtLeftTop else
|
|
if ((X < FixedWidth) and (Y > FixedHeight) and (ay < BodyHeight))
|
|
then Result := gtLeft else
|
|
if ((Y < FixedHeight) and (X > FixedWidth) and (ax < BodyWidth))
|
|
then Result := gtTop else
|
|
if ((X > FixedWidth) and (Y > FixedHeight) and (ax < BodyWidth) and (ay < BodyHeight))
|
|
then Result := gtCell;
|
|
|
|
end;
|
|
|
|
procedure TNiceGrid.Loaded;
|
|
begin
|
|
inherited;
|
|
Recalculate;
|
|
end;
|
|
|
|
procedure TNiceGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
|
|
Y: Integer);
|
|
var
|
|
Pt: TPoint;
|
|
|
|
begin
|
|
|
|
if not FEnabled then
|
|
begin
|
|
inherited;
|
|
Exit;
|
|
end;
|
|
|
|
case FGridState of
|
|
gsColSize:
|
|
begin
|
|
ForceHideCaret;
|
|
SizingColX := GetColCoord(SizingCol);
|
|
end;
|
|
gsBoxDrag:
|
|
begin
|
|
ForceHideCaret;
|
|
SmallBoxArea := FSelectArea;
|
|
end;
|
|
gsSelAll:
|
|
begin
|
|
FRow := 0;
|
|
FCol := 0;
|
|
BuffString := '';
|
|
EnsureVisible(0, 0);
|
|
FCol2 := ColCount-1;
|
|
FRow2 := FRowCount-1;
|
|
SetSelectArea(Rect(0, 0, ColCount-1, FRowCount-1));
|
|
ColRowChanged;
|
|
end;
|
|
gsSelRow:
|
|
begin
|
|
FRow := GetRowFromY(Y);
|
|
FCol := 0;
|
|
LastHover := FRow;
|
|
BuffString := '';
|
|
EnsureVisible(FCol, FRow);
|
|
FCol2 := ColCount-1;
|
|
FRow2 := FRow;
|
|
SmallBoxPos := 2;
|
|
AdjustSelection(Rect(0, FRow, ColCount-1, FRow), True);
|
|
ColRowChanged;
|
|
if Assigned(OnGutterClick)
|
|
then FOnGutterClick(Self, FRow, Button, Shift);
|
|
end;
|
|
gsSelCol:
|
|
begin
|
|
FCol := GetColFromX(X);
|
|
FRow := 0;
|
|
LastHover := FCol;
|
|
BuffString := '';
|
|
EnsureVisible(FCol, FRow);
|
|
FCol2 := FCol;
|
|
FRow2 := FRowCount-1;
|
|
SmallBoxPos := 1;
|
|
AdjustSelection(Rect(FCol, 0, FCol, FRowCount-1), True);
|
|
ColRowChanged;
|
|
if Assigned(FOnHeaderClick)
|
|
then FOnHeaderClick(Self, FCol, Button, Shift);
|
|
end;
|
|
gsCell:
|
|
begin
|
|
BuffString := '';
|
|
Pt := GetCellAtPos(X, Y);
|
|
if (Pt.X = FCol) and (Pt.Y = FRow) then
|
|
begin
|
|
EnsureVisible(FCol, FRow);
|
|
if (not FReadOnly) and (not FColumns[FCol].FReadOnly) then
|
|
begin
|
|
IsEditing := True;
|
|
FEdit.ShowEdit(FCol, FRow);
|
|
end;
|
|
end else
|
|
if (Pt.X <> -1) and (pt.Y <> -1) then
|
|
begin
|
|
EnsureVisible(Pt.X, Pt.Y);
|
|
FCol := Pt.X;
|
|
FRow := Pt.Y;
|
|
BuffString := '';
|
|
FCol2 := FCol;
|
|
FRow2 := FRow;
|
|
SetSelectArea(Rect(FCol, FRow, FCol, FRow));
|
|
end;
|
|
ColRowChanged;
|
|
end;
|
|
end;
|
|
(*
|
|
if (Cursor = crHSplit) then
|
|
begin
|
|
ForceHideCaret;
|
|
SizingColX := GetColCoord(SizingCol);
|
|
end else
|
|
|
|
if (Cursor = crSmallCross) then
|
|
begin
|
|
ForceHideCaret;
|
|
SmallBoxArea := FSelectArea;
|
|
end else
|
|
|
|
if (Cursor = crLeftTop) then
|
|
begin
|
|
FRow := 0;
|
|
FCol := 0;
|
|
BuffString := '';
|
|
EnsureVisible(0, 0);
|
|
FCol2 := ColCount-1;
|
|
FRow2 := FRowCount-1;
|
|
SetSelectArea(Rect(0, 0, ColCount-1, FRowCount-1));
|
|
ColRowChanged;
|
|
end else
|
|
|
|
if (Cursor = crRight) then
|
|
begin
|
|
FRow := GetRowFromY(Y);
|
|
FCol := 0;
|
|
LastHover := FRow;
|
|
BuffString := '';
|
|
EnsureVisible(FCol, FRow);
|
|
FCol2 := ColCount-1;
|
|
FRow2 := FRow;
|
|
SmallBoxPos := 2;
|
|
AdjustSelection(Rect(0, FRow, ColCount-1, FRow), True);
|
|
ColRowChanged;
|
|
if Assigned(OnGutterClick)
|
|
then FOnGutterClick(Self, FRow, Button, Shift);
|
|
end else
|
|
|
|
if (Cursor = crDown) then
|
|
begin
|
|
FCol := GetColFromX(X);
|
|
FRow := 0;
|
|
LastHover := FCol;
|
|
BuffString := '';
|
|
EnsureVisible(FCol, FRow);
|
|
FCol2 := FCol;
|
|
FRow2 := FRowCount-1;
|
|
SmallBoxPos := 1;
|
|
AdjustSelection(Rect(FCol, 0, FCol, FRowCount-1), True);
|
|
ColRowChanged;
|
|
if Assigned(FOnHeaderClick)
|
|
then FOnHeaderClick(Self, FCol, Button, Shift);
|
|
end else
|
|
|
|
if (Cursor = crPlus) then
|
|
begin
|
|
BuffString := '';
|
|
Pt := GetCellAtPos(X, Y);
|
|
if (Pt.X = FCol) and (Pt.Y = FRow) then
|
|
begin
|
|
EnsureVisible(FCol, FRow);
|
|
if (not FReadOnly) and (not FColumns[FCol].FReadOnly) then
|
|
begin
|
|
IsEditing := True;
|
|
FEdit.ShowEdit(FCol, FRow);
|
|
end;
|
|
end else
|
|
if (Pt.X <> -1) and (pt.Y <> -1) then
|
|
begin
|
|
EnsureVisible(Pt.X, Pt.Y);
|
|
FCol := Pt.X;
|
|
FRow := Pt.Y;
|
|
BuffString := '';
|
|
FCol2 := FCol;
|
|
FRow2 := FRow;
|
|
SetSelectArea(Rect(FCol, FRow, FCol, FRow));
|
|
end;
|
|
ColRowChanged;
|
|
end;
|
|
*)
|
|
SetCapture(Handle);
|
|
|
|
SetFocus;
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
procedure TNiceGrid.MouseMove(Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
Total2Col: Integer;
|
|
Suggested: Integer;
|
|
Pt: TPoint;
|
|
l, t, r, b: Integer;
|
|
i: Integer;
|
|
|
|
begin
|
|
if not FEnabled then
|
|
begin
|
|
FGridState := gsNormal;
|
|
Cursor := crDefault;
|
|
inherited;
|
|
Exit;
|
|
end;
|
|
|
|
if (ssLeft in Shift) then
|
|
begin
|
|
case FGridState of
|
|
gsCell:
|
|
begin
|
|
Pt := GetCellAtPos(X, Y);
|
|
if (Pt.X <> -1) and (Pt.Y <> -1) then
|
|
begin
|
|
l := Min(Pt.X, FCol);
|
|
t := Min(Pt.Y, FRow);
|
|
r := Max(Pt.X, FCol);
|
|
b := Max(Pt.Y, FRow);
|
|
FCol2 := Pt.X;
|
|
FRow2 := Pt.Y;
|
|
SetSelectArea(Rect(l, t, r, b));
|
|
EnsureVisible(FCol2, FRow2);
|
|
end;
|
|
end;
|
|
gsBoxDrag:
|
|
begin
|
|
Pt := GetCellAtPos(X, Y);
|
|
if (Pt.X <> -1) and (Pt.Y <> -1) then
|
|
begin
|
|
l := Min(Pt.X, SmallBoxArea.Left);
|
|
t := Min(Pt.Y, SmallBoxArea.Top);
|
|
r := Max(Pt.X, SmallBoxArea.Right);
|
|
b := Max(Pt.Y, SmallBoxArea.Bottom);
|
|
FCol2 := Pt.X;
|
|
FRow2 := Pt.Y;
|
|
SetSelectArea(Rect(l, t, r, b));
|
|
EnsureVisible(FCol2, FRow2);
|
|
end;
|
|
end;
|
|
gsSelRow:
|
|
begin
|
|
i := GetRowFromY(Y);
|
|
if (i <> -1) and (i <> LastHover) then
|
|
begin
|
|
LastHover := i;
|
|
t := Min(i, FRow);
|
|
b := Max(i, FRow);
|
|
FRow2 := i;
|
|
SmallBoxPos := 2;
|
|
AdjustSelection(Rect(0, t, ColCount-1, b), True);
|
|
end;
|
|
end;
|
|
gsSelCol:
|
|
begin
|
|
i := GetColFromX(X);
|
|
if (i <> -1) and (i <> LastHover) then
|
|
begin
|
|
LastHover := i;
|
|
l := Min(i, FCol);
|
|
r := Max(i, FCol);
|
|
FCol2 := i;
|
|
SmallBoxPos := 1;
|
|
AdjustSelection(Rect(l, 0, r, FRowCount-1), True);
|
|
end;
|
|
end;
|
|
gsColSize:
|
|
begin
|
|
Suggested := Max(5, X + FHorzOffset - SizingColX - FixedWidth);
|
|
if FFitToWidth then
|
|
begin
|
|
if (SizingCol = ColCount-1) or (SizingCol = -1) then
|
|
begin
|
|
inherited;
|
|
Exit;
|
|
end;
|
|
Total2Col := (ClientWidth - FixedWidth) - (TotalWidth - Columns[SizingCol].FWidth - Columns[SizingCol+1].FWidth);
|
|
if (Total2Col > 10) then
|
|
begin
|
|
Columns[SizingCol].FWidth := Suggested;
|
|
Columns[SizingCol+1].FWidth := Total2Col - Suggested;
|
|
end;
|
|
if (Columns[SizingCol+1].FWidth < 5) then
|
|
begin
|
|
Columns[SizingCol].FWidth := Total2Col - 5;
|
|
Columns[SizingCol+1].FWidth := 5;
|
|
end;
|
|
end else
|
|
begin
|
|
Columns[SizingCol].FWidth := Suggested;
|
|
end;
|
|
Recalculate;
|
|
InvalidateRightWard(FixedWidth);
|
|
end;
|
|
end;
|
|
(*
|
|
if (Cursor = crPlus) then
|
|
begin
|
|
Pt := GetCellAtPos(X, Y);
|
|
if (Pt.X <> -1) and (Pt.Y <> -1) then
|
|
begin
|
|
l := Min(Pt.X, FCol);
|
|
t := Min(Pt.Y, FRow);
|
|
r := Max(Pt.X, FCol);
|
|
b := Max(Pt.Y, FRow);
|
|
FCol2 := Pt.X;
|
|
FRow2 := Pt.Y;
|
|
SetSelectArea(Rect(l, t, r, b));
|
|
EnsureVisible(FCol2, FRow2);
|
|
end;
|
|
end else
|
|
|
|
if (Cursor = crSmallCross) then
|
|
begin
|
|
Pt := GetCellAtPos(X, Y);
|
|
if (Pt.X <> -1) and (Pt.Y <> -1) then
|
|
begin
|
|
l := Min(Pt.X, SmallBoxArea.Left);
|
|
t := Min(Pt.Y, SmallBoxArea.Top);
|
|
r := Max(Pt.X, SmallBoxArea.Right);
|
|
b := Max(Pt.Y, SmallBoxArea.Bottom);
|
|
FCol2 := Pt.X;
|
|
FRow2 := Pt.Y;
|
|
SetSelectArea(Rect(l, t, r, b));
|
|
EnsureVisible(FCol2, FRow2);
|
|
end;
|
|
end else
|
|
|
|
if (Cursor = crRight) then
|
|
begin
|
|
i := GetRowFromY(Y);
|
|
if (i <> -1) and (i <> LastHover) then
|
|
begin
|
|
LastHover := i;
|
|
t := Min(i, FRow);
|
|
b := Max(i, FRow);
|
|
FRow2 := i;
|
|
SmallBoxPos := 2;
|
|
AdjustSelection(Rect(0, t, ColCount-1, b), True);
|
|
end;
|
|
end else
|
|
|
|
if (Cursor = crDown) then
|
|
begin
|
|
i := GetColFromX(X);
|
|
if (i <> -1) and (i <> LastHover) then
|
|
begin
|
|
LastHover := i;
|
|
l := Min(i, FCol);
|
|
r := Max(i, FCol);
|
|
FCol2 := i;
|
|
SmallBoxPos := 1;
|
|
AdjustSelection(Rect(l, 0, r, FRowCount-1), True);
|
|
end;
|
|
end else
|
|
|
|
if (Cursor = crHSplit) then
|
|
begin
|
|
Suggested := Max(5, X + FHorzOffset - SizingColX - FixedWidth);
|
|
if FFitToWidth then
|
|
begin
|
|
if (SizingCol = ColCount-1) or (SizingCol = -1) then
|
|
begin
|
|
inherited;
|
|
Exit;
|
|
end;
|
|
Total2Col := (ClientWidth - FixedWidth) - (TotalWidth - Columns[SizingCol].FWidth - Columns[SizingCol+1].FWidth);
|
|
if (Total2Col > 10) then
|
|
begin
|
|
Columns[SizingCol].FWidth := Suggested;
|
|
Columns[SizingCol+1].FWidth := Total2Col - Suggested;
|
|
end;
|
|
if (Columns[SizingCol+1].FWidth < 5) then
|
|
begin
|
|
Columns[SizingCol].FWidth := Total2Col - 5;
|
|
Columns[SizingCol+1].FWidth := 5;
|
|
end;
|
|
end else
|
|
begin
|
|
Columns[SizingCol].FWidth := Suggested;
|
|
end;
|
|
Recalculate;
|
|
InvalidateRightWard(FixedWidth);
|
|
end; *)
|
|
|
|
end else
|
|
begin
|
|
Cursor := CursorArray[GetHitTestInfo(X, Y)];
|
|
FGridState := GridStateArray[GetHitTestInfo(X, Y)];
|
|
end;
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
procedure TNiceGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
|
|
Y: Integer);
|
|
var
|
|
Ls: TList;
|
|
ax, ay: Integer;
|
|
l, t, w, h: Integer;
|
|
|
|
function GetCopy(nx, ny: Integer): string;
|
|
var
|
|
ix, iy: Integer;
|
|
begin
|
|
ix := nx;
|
|
iy := ny;
|
|
while (ix < l)
|
|
do ix := ix + w;
|
|
while (iy < t)
|
|
do iy := iy + h;
|
|
ix := ((ix - l) mod w) + l;
|
|
iy := ((iy - t) mod h) + t;
|
|
Result := SafeGetCell(TNiceColumn(Ls[ix]).Index, iy);
|
|
end;
|
|
|
|
begin
|
|
if (Cursor = crSmallCross) then
|
|
begin
|
|
if FReadOnly then
|
|
begin
|
|
SmallBoxArea := Rect(-1, -1, -1, -1);
|
|
InvalidateCells;
|
|
end else
|
|
begin
|
|
FUpdating := True;
|
|
Ls := TList.Create;
|
|
for ax := FSelectArea.Left to FSelectArea.Right do
|
|
if FColumns[ax].FVisible
|
|
then Ls.Add(FColumns[ax]);
|
|
l := 0;
|
|
for ax := 0 to Ls.Count-1 do
|
|
begin
|
|
if (TNiceColumn(Ls[ax]).Index = SmallBoxArea.Left) then
|
|
begin
|
|
l := ax;
|
|
Break;
|
|
end;
|
|
end;
|
|
t := SmallBoxArea.Top;
|
|
w := (SmallBoxArea.Right - SmallBoxArea.Left) + 1;
|
|
h := (SmallBoxArea.Bottom - SmallBoxArea.Top) + 1;
|
|
for ax := 0 to Ls.Count-1 do
|
|
for ay := FSelectArea.Top to FSelectArea.Bottom
|
|
do InternalSetCell(TNiceColumn(Ls[ax]).Index, ay, GetCopy(ax, ay), True);
|
|
Ls.Free;
|
|
SmallBoxArea := Rect(-1, -1, -1, -1);
|
|
BuffString := '';
|
|
FUpdating := False;
|
|
InvalidateCells;
|
|
end;
|
|
end;
|
|
|
|
Cursor := CursorArray[GetHitTestInfo(X, Y)];
|
|
FGridState := GridStateArray[GetHitTestInfo(X, Y)];
|
|
ReleaseCapture;
|
|
LastHover := -1;
|
|
|
|
inherited;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetColumns(Value: TNiceColumns);
|
|
begin
|
|
FColumns.Assign(Value);
|
|
end;
|
|
|
|
function TNiceGrid.CreateColumn: TNiceColumn;
|
|
begin
|
|
Result := TNiceColumn.Create(Columns);
|
|
end;
|
|
|
|
procedure TNiceGrid.UpdateColumn(Index: Integer);
|
|
var
|
|
l, w: Integer;
|
|
Rc: TRect;
|
|
begin
|
|
l := GetColCoord(Index);
|
|
w := FColumns[Index].FWidth;
|
|
Rc := Rect(l - 3, 0, l + w + 3, ClientHeight);
|
|
InvalidateRect(Handle, @Rc, False);
|
|
end;
|
|
|
|
procedure TNiceGrid.UpdateColumns;
|
|
begin
|
|
UpdateHeader;
|
|
Invalidate;
|
|
end;
|
|
|
|
function TNiceGrid.GetColCount: Integer;
|
|
begin
|
|
Result := FColumns.Count;
|
|
end;
|
|
|
|
function TNiceGrid.TotalWidth: Integer;
|
|
var
|
|
x: Integer;
|
|
begin
|
|
Result := 0;
|
|
for x := 0 to FColumns.Count-1 do
|
|
begin
|
|
if FColumns[x].FVisible
|
|
then Result := Result + FColumns[x].FWidth;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.CMFontChanged(var Msg: {$IFDEF FPC}TLMessage{$ELSE}TMessage{$ENDIF});
|
|
var
|
|
x: Integer;
|
|
begin
|
|
inherited;
|
|
for x := 0 to FColumns.Count-1
|
|
do FColumns[x].Font.Assign(Font);
|
|
end;
|
|
|
|
procedure TNiceGrid.WMSize(var Msg: {$IFDEF FPC}TLMessage{$ELSE}TMessage{$ENDIF});
|
|
begin
|
|
inherited;
|
|
Recalculate;
|
|
if (FColumns.Count > 0)
|
|
then EnsureVisible(FCol, FRow);
|
|
end;
|
|
|
|
procedure TNiceGrid.WMEraseBkgnd(var Msg: {$IFDEF FPC}TLMEraseBkGnd{$ELSE}TWMEraseBkGnd{$ENDIF});
|
|
begin
|
|
Msg.Result := 1;
|
|
end;
|
|
|
|
procedure TNiceGrid.CMWantSpecialKey(var Message: {$IFDEF FPC}TLMKey{$ELSE}TWMKey{$ENDIF});
|
|
begin
|
|
inherited;
|
|
with Message do
|
|
case CharCode of
|
|
VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN:
|
|
Result := 1;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetShowGrid(Value: Boolean);
|
|
begin
|
|
if (FShowGrid <> Value) then
|
|
begin
|
|
FShowGrid := Value;
|
|
InvalidateCells;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetShowFooter(const Value: Boolean);
|
|
begin
|
|
if (FShowFooter <> Value) then
|
|
begin
|
|
FShowFooter := Value;
|
|
Recalculate;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.Clear;
|
|
var
|
|
x: Integer;
|
|
begin
|
|
for x := 0 to ColCount-1
|
|
do FColumns[x].FStrings.Clear;
|
|
InvalidateCells;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetHorzOffset(Value: Integer);
|
|
begin
|
|
if (FHorzOffset <> Value) then
|
|
begin
|
|
FHorzOffset := Max(0, Min(FMaxHScroll, Value));
|
|
SetScrollBar(SB_HORZ, 0, FHorzOffset, SIF_POS);
|
|
InvalidateRightWard(FixedWidth);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetVertOffset(Value: Integer);
|
|
begin
|
|
if (FVertOffset <> Value) then
|
|
begin
|
|
FVertOffset := Max(0, Min(FMaxVScroll, Value));
|
|
NormalizeVertOffset;
|
|
SetScrollBar(SB_VERT, 0, FVertOffset, SIF_POS);
|
|
InvalidateDownWard(FixedHeight);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.EnsureVisible(X, Y: Integer);
|
|
var
|
|
t, b, h: Integer;
|
|
l, r: Integer;
|
|
Horz, Vert: Boolean;
|
|
SuggestedHorz, SuggestedVert: Integer;
|
|
|
|
begin
|
|
|
|
if (X = -1) or (Y = -1)
|
|
then Exit;
|
|
|
|
if (AllWidth < ClientWidth) and (AllHeight < ClientHeight)
|
|
then Exit;
|
|
|
|
SuggestedVert := FVertOffset;
|
|
t := FVertOffset div FDefRowHeight;
|
|
h := ((ClientHeight - FixedHeight) div FDefRowHeight) - 1;
|
|
if FShowFooter
|
|
then h := h-1;
|
|
b := t + h;
|
|
Vert := (Y < t) or (Y > b);
|
|
if (Y < t)
|
|
then SuggestedVert := Y * FDefRowHeight;
|
|
if (Y > b)
|
|
then SuggestedVert := (Y - h) * FDefRowHeight;
|
|
|
|
SuggestedHorz := FHorzOffset;
|
|
l := GetColCoord(X) - FHorzOffset + FixedWidth;
|
|
r := l + FColumns[x].FWidth;
|
|
Horz := (l < FixedWidth) or (r > ClientWidth);
|
|
if (l < FixedWidth)
|
|
then SuggestedHorz := Max(0, SuggestedHorz + (l - FixedWidth));
|
|
if (r > ClientWidth)
|
|
then SuggestedHorz := Min(FMaxHScroll, SuggestedHorz - (ClientWidth - r) + 1);
|
|
|
|
if Vert and not Horz
|
|
then SetVertOffset(SuggestedVert) else
|
|
|
|
if Horz and not Vert
|
|
then SetHorzOffset(SuggestedHorz) else
|
|
|
|
if Horz and Vert
|
|
then
|
|
begin
|
|
FHorzOffset := SuggestedHorz;
|
|
FVertOffset := SuggestedVert;
|
|
SetScrollBar(SB_HORZ, 0, FHorzOffset, SIF_POS);
|
|
SetScrollBar(SB_VERT, 0, FVertOffset, SIF_POS);
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
function TNiceGrid.HeaderCells(I: Integer): THeaderInfo;
|
|
begin
|
|
Result := PHeaderInfo(FHeaderInfos[I])^;
|
|
end;
|
|
|
|
function TNiceGrid.HeaderCellsCount: Integer;
|
|
begin
|
|
Result := FHeaderInfos.Count;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetReadOnly(Value: Boolean);
|
|
begin
|
|
if (FReadOnly <> Value) then
|
|
begin
|
|
FReadOnly := Value;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetCol(Value: Integer);
|
|
begin
|
|
if (FCol <> Value) then
|
|
begin
|
|
ForceHideCaret;
|
|
FCol := Value;
|
|
FCol2 := Value;
|
|
FRow2 := FRow;
|
|
BuffString := '';
|
|
SetSelectArea(Rect(FCol, FRow, FCol, FRow));
|
|
InvalidateRightWard(FixedWidth);
|
|
ColRowChanged;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetRow(Value: Integer);
|
|
begin
|
|
if (FRow <> Value) then
|
|
begin
|
|
ForceHideCaret;
|
|
FRow := Value;
|
|
FRow2 := Value;
|
|
FCol2 := FCol;
|
|
BuffString := '';
|
|
SetSelectArea(Rect(FCol, FRow, FCol, FRow));
|
|
InvalidateDownWard(FixedHeight);
|
|
ColRowChanged;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.AdjustSelection(Value: TRect; Force: Boolean);
|
|
var
|
|
Old, Rc: TRect;
|
|
R1, R2, R: TRect;
|
|
begin
|
|
|
|
if EqualRect(FSelectArea, Value) and not Force
|
|
then Exit;
|
|
|
|
ForceHideCaret;
|
|
Old := FSelectArea;
|
|
FSelectArea := Value;
|
|
|
|
Rc.Left := Min(Old.Left, FSelectArea.Left);
|
|
Rc.Top := Min(Old.Top, FSelectArea.Top);
|
|
Rc.Right := Max(Old.Right, FselectArea.Right);
|
|
Rc.Bottom := Max(Old.Bottom, FSelectArea.Bottom);
|
|
|
|
R1 := GetCellRect(Rc.Left, Rc.Top);
|
|
R2 := GetCellRect(Rc.Right, Rc.Bottom);
|
|
R := Rect(R1.Left, R1.Top, R2.Right, R2.Bottom);
|
|
OffsetRect(R, - FHorzOffset + FixedWidth, - FVertOffset + FixedHeight);
|
|
|
|
InflateRect(R, 3, 3);
|
|
InvalidateRect(Handle, @R, False);
|
|
|
|
if (FGutterKind = gkPointer) then
|
|
begin
|
|
R := Rect(0, FixedHeight, FixedWidth, ClientHeight);
|
|
InvalidateRect(Handle, @R, False);
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TNiceGrid.SetSelectArea(Value: TRect);
|
|
begin
|
|
AdjustSelection(Value, False);
|
|
end;
|
|
|
|
|
|
var
|
|
CaretVisible: Boolean = False;
|
|
|
|
// I don't think MS's HideCaret and ShowCaret mechanism was a good idea.
|
|
|
|
procedure TNiceGrid.ForceHideCaret;
|
|
begin
|
|
if CaretVisible
|
|
then HideCaret(Handle);
|
|
CaretVisible := False;
|
|
FEdit.HideEdit;
|
|
end;
|
|
|
|
procedure TNiceGrid.ForceShowCaret;
|
|
begin
|
|
if not CaretVisible
|
|
then ShowCaret(Handle);
|
|
CaretVisible := True;
|
|
end;
|
|
|
|
procedure TNiceGrid.WMKillFocus(var Msg: {$IFDEF FPC}TLMKillFocus{$ELSE}TWMKillFocus{$ENDIF});
|
|
begin
|
|
if FEdit.HandleAllocated then
|
|
begin
|
|
if (Msg.FocusedWnd <> FEdit.Handle)
|
|
then ForceHideCaret;
|
|
DestroyCaret{$IFDEF FPC}(FEdit.Handle){$ENDIF};
|
|
end;
|
|
CaretVisible := False;
|
|
if not IsEditing
|
|
then InvalidateCells;
|
|
end;
|
|
|
|
procedure TNiceGrid.WMSetFocus(var Msg: {$IFDEF FPC}TLMSetFocus{$ELSE}TWMSetFocus{$ENDIF});
|
|
begin
|
|
Unused(Msg);
|
|
CreateCaret(Handle, 0, 1, FDefRowHeight - 2);
|
|
CaretVisible := False;
|
|
InvalidateCells;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetGutterKind(Value: TGutterKind);
|
|
var
|
|
Rc: TRect;
|
|
RedrawAll: Boolean;
|
|
Old: TGutterKind;
|
|
begin
|
|
Old := FGutterKind;
|
|
if (FGutterKind <> Value) then
|
|
begin
|
|
FGutterKind := Value;
|
|
Recalculate;
|
|
RedrawAll := (Old = gkNone) or (Value = gkNone);
|
|
if RedrawAll then
|
|
begin
|
|
Invalidate;
|
|
end else
|
|
begin
|
|
Rc := Rect(0, FixedHeight, FixedWidth, ClientHeight);
|
|
InvalidateRect(Handle, @Rc, False);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetGutterWidth(Value: Integer);
|
|
begin
|
|
if (FGutterWidth <> Value) then
|
|
begin
|
|
FGutterWidth := Value;
|
|
Recalculate;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.CopyToClipboard;
|
|
var
|
|
s: string;
|
|
t: TStringList;
|
|
x, y: Integer;
|
|
begin
|
|
t := TStringList.Create;
|
|
for y := FSelectArea.Top to FSelectArea.Bottom do
|
|
begin
|
|
s := '';
|
|
for x := FSelectArea.Left to FSelectArea.Right do
|
|
begin
|
|
if FColumns[x].FVisible then
|
|
begin
|
|
if (x = FSelectArea.Left)
|
|
then s := SafeGetCell(X, Y)
|
|
else s := s + #9 + SafeGetCell(X, Y);
|
|
end;
|
|
end;
|
|
t.Add(s);
|
|
end;
|
|
Clipboard.AsText := t.Text;
|
|
t.Free;
|
|
end;
|
|
|
|
procedure TNiceGrid.CutToClipboard;
|
|
var
|
|
s: string;
|
|
t: TStringList;
|
|
x, y: Integer;
|
|
begin
|
|
FUpdating := True;
|
|
t := TStringList.Create;
|
|
for y := FSelectArea.Top to FSelectArea.Bottom do
|
|
begin
|
|
s := '';
|
|
for x := FSelectArea.Left to FSelectArea.Right do
|
|
begin
|
|
if FColumns[x].FVisible then
|
|
begin
|
|
if (x = FSelectArea.Left)
|
|
then s := SafeGetCell(X, Y)
|
|
else s := s + #9 + SafeGetCell(X, Y);
|
|
InternalSetCell(X, Y, '', True);
|
|
end;
|
|
end;
|
|
t.Add(s);
|
|
end;
|
|
Clipboard.AsText := t.Text;
|
|
t.Free;
|
|
FUpdating := False;
|
|
InvalidateCells;
|
|
end;
|
|
|
|
procedure TNiceGrid.PasteFromClipboard;
|
|
var
|
|
tr, tc: TStringList;
|
|
x, y: Integer;
|
|
s: string;
|
|
n: Integer;
|
|
TabCnt: Integer;
|
|
ax, ay: Integer;
|
|
ColCnt: Integer;
|
|
|
|
begin
|
|
|
|
if not Clipboard.HasFormat(CF_TEXT)
|
|
then Exit;
|
|
|
|
ForceHideCaret;
|
|
|
|
FUpdating := True;
|
|
tr := TStringList.Create;
|
|
tc := TStringList.Create;
|
|
tr.Text := Clipboard.AsText;
|
|
TabCnt := 1;
|
|
|
|
for y := 0 to tr.Count-1 do
|
|
begin
|
|
n := 1;
|
|
s := tr[y];
|
|
for x := 1 to Length(s) do
|
|
if (s[x] = #9)
|
|
then Inc(n);
|
|
TabCnt := Max(TabCnt, n);
|
|
end;
|
|
|
|
ColCnt := ColCount; // Just to make it fast
|
|
|
|
if (FSelectArea.Left = FSelectArea.Right) and (FSelectArea.Top = FSelectArea.Bottom) then
|
|
begin
|
|
|
|
for y := 0 to tr.Count-1 do
|
|
begin
|
|
tc.Text := StringReplace(tr[y], #9, #13#10, [rfReplaceAll]);
|
|
while (tc.Count < TabCnt)
|
|
do tc.Add('');
|
|
x := 0;
|
|
ax := FCol;
|
|
while (x < tc.Count) do
|
|
begin
|
|
ay := FRow + y;
|
|
if FColumns[ax].FVisible then
|
|
begin
|
|
if (ax < ColCnt) and (ay < FRowCount)
|
|
then InternalSetCell(ax, ay, tc[x], True);
|
|
Inc(x);
|
|
end;
|
|
Inc(ax);
|
|
end;
|
|
end;
|
|
|
|
end else
|
|
begin
|
|
|
|
ay := FSelectArea.Top;
|
|
while (ay <= FSelectArea.Bottom) do
|
|
begin
|
|
tc.Text := StringReplace(tr[(ay - FSelectArea.Top) mod tr.Count], #9, #13#10, [rfReplaceAll]);
|
|
while (tc.Count < TabCnt)
|
|
do tc.Add('');
|
|
ax := FSelectArea.Left;
|
|
x := 0;
|
|
while (ax <= FSelectArea.Right) do
|
|
begin
|
|
if FColumns[ax].FVisible then
|
|
begin
|
|
InternalSetCell(ax, ay, tc[x], True);
|
|
Inc(x);
|
|
if (x = tc.Count)
|
|
then x := 0;
|
|
end;
|
|
Inc(ax);
|
|
end;
|
|
Inc(ay);
|
|
end;
|
|
|
|
end;
|
|
|
|
tr.Free;
|
|
tc.Free;
|
|
|
|
FUpdating := False;
|
|
InvalidateCells;
|
|
|
|
end;
|
|
|
|
procedure TNiceGrid.InvalidateCells;
|
|
var
|
|
Rc: TRect;
|
|
begin
|
|
Rc := Rect(FixedWidth-2, FixedHeight-2, ClientWidth, ClientHeight);
|
|
InvalidateRect(Handle, @Rc, False);
|
|
end;
|
|
|
|
procedure TNiceGrid.InvalidateDownWard(Top: Integer);
|
|
var
|
|
Rc: TRect;
|
|
begin
|
|
Rc := Rect(0, Top, ClientWidth, ClientHeight);
|
|
InvalidateRect(Handle, @Rc, False);
|
|
end;
|
|
|
|
procedure TNiceGrid.InvalidateRightWard(Left: Integer);
|
|
var
|
|
Rc: TRect;
|
|
begin
|
|
Rc := Rect(Left, 0, ClientWidth, ClientHeight);
|
|
InvalidateRect(Handle, @Rc, False);
|
|
end;
|
|
|
|
procedure TNiceGrid.NormalizeVertOffset;
|
|
begin
|
|
FVertOffset := (FVertOffset div FDefRowHeight) * FDefRowHeight;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetGutterFont(Value: TFont);
|
|
begin
|
|
FGutterFont.Assign(Value);
|
|
InvalidateGutter;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetHeaderFont(Value: TFont);
|
|
begin
|
|
FHeaderFont.Assign(Value);
|
|
InvalidateHeader;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetFooterFont(const Value: TFont);
|
|
begin
|
|
FFooterFont.Assign(Value);
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TNiceGrid.InvalidateGutter;
|
|
var
|
|
Rc: TRect;
|
|
begin
|
|
Rc := Rect(0, FixedHeight, FixedWidth, ClientHeight);
|
|
InvalidateRect(Handle, @Rc, False);
|
|
end;
|
|
|
|
procedure TNiceGrid.InvalidateHeader;
|
|
var
|
|
Rc: TRect;
|
|
begin
|
|
Rc := Rect(0, 0, ClientWidth, FixedHeight);
|
|
InvalidateRect(Handle, @Rc, False);
|
|
end;
|
|
|
|
procedure TNiceGrid.HeaderFontChange(Sender: TObject);
|
|
begin
|
|
InvalidateHeader;
|
|
end;
|
|
|
|
procedure TNiceGrid.GutterFontChange(Sender: TObject);
|
|
begin
|
|
InvalidateGutter;
|
|
end;
|
|
|
|
procedure TNiceGrid.FooterFontChange(Sender: TObject);
|
|
begin
|
|
Invalidate;
|
|
end;
|
|
|
|
function TNiceGrid.GetFirstVisible: Integer;
|
|
var
|
|
x: Integer;
|
|
begin
|
|
Result := -1;
|
|
if (ColCount > 0) then
|
|
begin
|
|
for x := 0 to ColCount-1 do
|
|
begin
|
|
if Columns[x].Visible then
|
|
begin
|
|
Result := x;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TNiceGrid.GetLastVisible: Integer;
|
|
var
|
|
x: Integer;
|
|
begin
|
|
Result := -1;
|
|
if (ColCount > 0) then
|
|
begin
|
|
for x := ColCount-1 downto 0 do
|
|
begin
|
|
if Columns[x].Visible then
|
|
begin
|
|
Result := x;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TNiceGrid.GetNextVisible(Index: Integer): Integer;
|
|
var
|
|
x: Integer;
|
|
begin
|
|
Result := Index;
|
|
if (ColCount > 0) and (Index < ColCount) then
|
|
begin
|
|
for x := (Index + 1) to (ColCount - 1) do
|
|
begin
|
|
if Columns[x].Visible then
|
|
begin
|
|
Result := x;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TNiceGrid.GetPrevVisible(Index: Integer): Integer;
|
|
var
|
|
x: Integer;
|
|
begin
|
|
Result := Index;
|
|
if (ColCount > 0) and (Index > 0) then
|
|
begin
|
|
for x := (Index - 1) downto 0 do
|
|
begin
|
|
if Columns[x].Visible then
|
|
begin
|
|
Result := x;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.DeleteRow(ARow: Integer);
|
|
var
|
|
x, y: Integer;
|
|
begin
|
|
ForceHideCaret;
|
|
if (ARow >= 0) and (ARow < FRowCount) then
|
|
begin
|
|
for x := 0 to ColCount-1 do
|
|
begin
|
|
with FColumns[x].Strings do
|
|
begin
|
|
if (Count > ARow) then
|
|
begin
|
|
for y := ARow to Count-2
|
|
do Strings[y] := Strings[y + 1];
|
|
Strings[Count-1] := '';
|
|
end;
|
|
end;
|
|
end;
|
|
if (FRow = FRowCount-1)
|
|
then Dec(FRow);
|
|
RowCount := RowCount - 1;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.InsertRow(ARow: Integer);
|
|
var
|
|
x: Integer;
|
|
begin
|
|
ForceHideCaret;
|
|
if (ARow >= 0) and (ARow < FRowCount) then
|
|
begin
|
|
for x := 0 to ColCount-1 do
|
|
begin
|
|
with FColumns[x].Strings do
|
|
begin
|
|
while (Count < ARow)
|
|
do Add('');
|
|
Insert(ARow, '');
|
|
end;
|
|
end;
|
|
RowCount := RowCount + 1;
|
|
end;
|
|
end;
|
|
|
|
function TNiceGrid.AddRow: Integer;
|
|
var
|
|
x: Integer;
|
|
n: Integer;
|
|
begin
|
|
ForceHideCaret;
|
|
n := FRowCount + 1;
|
|
for x := 0 to ColCount-1 do
|
|
begin
|
|
with FColumns[x].Strings do
|
|
begin
|
|
while (Count < n)
|
|
do Add('');
|
|
Strings[FRowCount] := '';
|
|
end;
|
|
end;
|
|
RowCount := RowCount + 1;
|
|
Result := FRowCount-1;
|
|
end;
|
|
|
|
|
|
// This is a workaround to avoid mess up with accelerators.
|
|
// NiceGrid was unable to capture keyboard event of chars that already
|
|
// defined as accelerator of another control.
|
|
// (Char after '&' (ampersand) in ex. TButton.Caption, TMenuItem.Caption, etc.)
|
|
// Don't know why and how this workaround works, but this is found after
|
|
// spying with WinSight. WM_USER + $B902 - mPri-
|
|
|
|
procedure TNiceGrid.WMUnknown(var Msg: {$IFDEF FPC}TLMessage{$ELSE}TMessage{$ENDIF});
|
|
begin
|
|
Msg.Result := 0;
|
|
end;
|
|
|
|
procedure TNiceGrid.WMMouseWheel(var Msg: {$IFDEF FPC}TLMessage{$ELSE}TWMMouseWheel{$ENDIF});
|
|
var
|
|
Old: Integer;
|
|
begin
|
|
Old := FVertOffset;
|
|
FVertOffset := Max(0, Min(FMaxVScroll, FVertOffset - Msg.{$IFDEF FPC}lParam{$ELSE}WheelDelta{$ENDIF}));
|
|
if (FVertOffset <> Old) then
|
|
begin
|
|
SetScrollBar(SB_VERT, 0, FVertOffset, SIF_POS);
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.ColRowChanged;
|
|
begin
|
|
if Assigned(Sync)
|
|
then Sync.Row := FRow;
|
|
if Assigned(FOnColRowChanged)
|
|
then FOnColRowChanged(Self, FCol, FRow);
|
|
end;
|
|
|
|
procedure TNiceGrid.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
if (AComponent = Sync) and (Operation = opRemove)
|
|
then Sync := nil;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetGutterStrings(const Value: TStrings);
|
|
begin
|
|
FGutterStrings.Assign(Value);
|
|
if (FGutterKind = gkString)
|
|
then InvalidateGutter;
|
|
end;
|
|
|
|
function TNiceGrid.GetObject(X, Y: Integer): TObject;
|
|
var
|
|
t: TStrings;
|
|
begin
|
|
Result := nil;
|
|
if (X > ColCount-1) or (Y > FRowCount-1)
|
|
then raise Exception.Create('Cell Index out of bound.');
|
|
t := Columns[X].FStrings;
|
|
if (Y < t.Count)
|
|
then Result := t.Objects[Y];
|
|
end;
|
|
|
|
procedure TNiceGrid.SetObject(X, Y: Integer; const Value: TObject);
|
|
var
|
|
t: TStrings;
|
|
begin
|
|
if (X > ColCount-1) or (Y > FRowCount-1)
|
|
then raise Exception.Create('Cell Index out of bound.');
|
|
t := Columns[X].FStrings;
|
|
while (Y > t.Count-1)
|
|
do t.Add('');
|
|
t.Objects[Y] := Value;
|
|
end;
|
|
|
|
procedure TNiceGrid.ClearMergeCells;
|
|
var
|
|
x, y: Integer;
|
|
List: TStrings;
|
|
begin
|
|
for x := 0 to FColumns.Count-1 do
|
|
begin
|
|
List := FColumns[x].FStrings;
|
|
for y := 0 to List.Count-1
|
|
do List.Objects[y] := nil;
|
|
end;
|
|
for x := 0 to Mergeds.Count-1
|
|
do TMergeCell(Mergeds[x]).Free;
|
|
Mergeds.Clear;
|
|
end;
|
|
|
|
function TNiceGrid.MergeCells(const X1, Y1, X2, Y2: Integer;
|
|
ACaption: string): TMergeCell;
|
|
begin
|
|
Result := TMergeCell.Create;
|
|
Result.Font.Assign(Font);
|
|
Result.Color := Color;
|
|
Result.Caption := ACaption;
|
|
Result.HorzAlign := haCenter;
|
|
Result.VertAlign := vaCenter;
|
|
Result.Rc := Rect(Min(X1, X2), Min(Y1, Y2), Max(X1, X2), Max(Y1, Y2));
|
|
Mergeds.Add(Result);
|
|
if not FUpdating then
|
|
begin
|
|
Recalculate;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.BuildMergeData;
|
|
var
|
|
Rc: TRect;
|
|
x, y, z: Integer;
|
|
begin
|
|
for x := 0 to Mergeds.Count-1 do
|
|
begin
|
|
Rc := TMergeCell(Mergeds[x]).Rc;
|
|
for y := Rc.Left to Rc.Right do
|
|
begin
|
|
if (y >= FColumns.Count)
|
|
then Continue;
|
|
for z := Rc.Top to Rc.Bottom do
|
|
begin
|
|
InternalSetCell(y, z, '', False);
|
|
SetObject(y, z, TObject(MergeID));
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.DrawMergedCell(Index: Integer);
|
|
var
|
|
Data: TMergeCell;
|
|
Rc, Dummy: TRect;
|
|
l1, l2, t, h: Integer;
|
|
begin
|
|
Data := TMergeCell(Mergeds[Index]);
|
|
l1 := GetColCoord(Data.Rc.Left);
|
|
l2 := GetColCoord(Data.Rc.Right + 1);
|
|
t := FDefRowHeight * Data.Rc.Top;
|
|
h := FDefRowHeight * (Data.Rc.Bottom - Data.Rc.Top + 1);
|
|
Rc := Rect(l1-1, t-1, l2, t+h);
|
|
OffsetRect(Rc, -FHorzOffset + FixedWidth, -FVertOffset + FixedHeight);
|
|
Dummy := Rect(0, 0, 0, 0); // to silence the compiler
|
|
if IntersectRect(Dummy, Rc, CellBox) then
|
|
begin
|
|
with Canvas do
|
|
begin
|
|
Font.Assign(Data.Font);
|
|
if not FEnabled
|
|
then Font.Color := FGridColor;
|
|
Pen.Color := FGridColor;
|
|
Brush.Color := Data.Color;
|
|
Brush.Style := bsSolid;
|
|
if FShowGrid
|
|
then Rectangle(Rc)
|
|
else FillRect(Rc);
|
|
Brush.Style := bsClear;
|
|
InflateRect(Rc, -4, -2);
|
|
DrawString(Canvas, Data.Caption, Rc, Data.HorzAlign, Data.VertAlign, False);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TNiceGrid.GetHeaderInfo: TList;
|
|
begin
|
|
Result := FHeaderInfos;
|
|
end;
|
|
|
|
function TNiceGrid.GetMergedCellsData: TList;
|
|
begin
|
|
Result := Mergeds;
|
|
end;
|
|
|
|
procedure TNiceGrid.SetEnabled(const Value: Boolean);
|
|
begin
|
|
if (FEnabled <> Value) then
|
|
begin
|
|
FEnabled := Value;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF FPC}
|
|
// Handle Lazarus' High-DPI scaling
|
|
procedure TNiceGrid.DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
|
|
const AXProportion, AYProportion: Double);
|
|
var
|
|
i: Integer;
|
|
col: TNiceColumn;
|
|
begin
|
|
inherited DoAutoAdjustLayout(AMode, AXProportion, AYProportion);
|
|
if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then
|
|
begin
|
|
FDefColWidth := round(FDefColWidth * AYProportion);
|
|
FDefRowHeight := round(FDefRowHeight * AYProportion);
|
|
FGutterWidth := round(FGutterWidth * AXProportion);
|
|
for i := 0 to Columns.Count-1 do
|
|
begin
|
|
col := Columns[i];
|
|
col.Width := round(col.Width * AXProportion);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.FixDesignFontsPPI(const ADesignTimePPI: Integer);
|
|
var
|
|
i: Integer;
|
|
col: TNiceColumn;
|
|
begin
|
|
inherited;
|
|
DoFixDesignFontPPI(FHeaderFont, ADesignTimePPI);
|
|
DoFixDesignFontPPI(FGutterFont, ADesignTimePPI);
|
|
DoFixDesignFontPPI(FFooterFont, ADesignTimePPI);
|
|
for i := 0 to Columns.Count-1 do
|
|
begin
|
|
col := Columns[i];
|
|
DoFixDesignFontPPI(col.Font, ADesignTimePPI);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGrid.ScaleFontsPPI(const AToPPI: Integer; const AProportion: Double);
|
|
var
|
|
i: Integer;
|
|
col: TNiceColumn;
|
|
begin
|
|
inherited;
|
|
DoScaleFontPPI(FHeaderFont, AToPPI, AProportion);
|
|
DoScaleFontPPI(FGutterFont, AToPPI, AProportion);
|
|
DoScaleFontPPI(FFooterFont, AToPPI, AProportion);
|
|
for i := 0 to Columns.Count-1 do
|
|
begin
|
|
col := Columns[i];
|
|
DoScaleFontPPI(col.Font, AToPPI, AProportion);
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
{ TNiceColumn }
|
|
|
|
constructor TNiceColumn.Create(Collection: TCollection);
|
|
begin
|
|
FStrings := TStringList.Create;
|
|
FFont := TFont.Create;
|
|
FHorzAlign := haLeft;
|
|
FVertAlign := vaCenter;
|
|
FVisible := True;
|
|
FCanResize := True;
|
|
FReadOnly := False;
|
|
FTag := 0;
|
|
FTag2 := 0;
|
|
with TNiceColumns(Collection).Grid do
|
|
begin
|
|
Self.FFont.Assign(Font);
|
|
Self.FWidth := DefColWidth;
|
|
Self.FColor := Color;
|
|
end;
|
|
FFont.OnChange := FontChange;
|
|
inherited Create(Collection);
|
|
end;
|
|
|
|
destructor TNiceColumn.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FFont.Free;
|
|
FStrings.Free;
|
|
end;
|
|
|
|
procedure TNiceColumn.Assign(Source: TPersistent);
|
|
begin
|
|
if (Source is TNiceColumn) then
|
|
begin
|
|
Title := TNiceColumn(Source).Title;
|
|
Footer := TNiceColumn(Source).Footer;
|
|
Width := TNiceColumn(Source).Width;
|
|
Font := TNiceColumn(Source).Font;
|
|
Color := TNiceColumn(Source).Color;
|
|
HorzAlign := TNiceColumn(Source).HorzAlign;
|
|
VertAlign := TNiceColumn(Source).VertAlign;
|
|
Visible := TNiceColumn(Source).Visible;
|
|
Tag := TNiceColumn(Source).Tag;
|
|
Tag2 := TNiceColumn(Source).Tag2;
|
|
Hint := TNiceColumn(Source).Hint;
|
|
CanResize := TNiceColumn(Source).CanResize;
|
|
ReadOnly := TNiceColumn(Source).ReadOnly;
|
|
Strings.Assign(TNiceColumn(Source).Strings);
|
|
Changed(False);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceColumn.SetColor(Value: TColor);
|
|
begin
|
|
if (FColor <> Value) then
|
|
begin
|
|
FColor := Value;
|
|
Changed(False);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceColumn.SetFont(Value: TFont);
|
|
begin
|
|
FFont.Assign(Value);
|
|
Changed(False);
|
|
end;
|
|
|
|
procedure TNiceColumn.SetHorzAlign(Value: THorzAlign);
|
|
begin
|
|
if (FHorzAlign <> Value) then
|
|
begin
|
|
FHorzAlign := Value;
|
|
Changed(False);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceColumn.SetTitle(Value: string);
|
|
begin
|
|
if (FTitle <> Value) then
|
|
begin
|
|
FTitle := Value;
|
|
Changed(True);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceColumn.SetFooter(const Value: string);
|
|
begin
|
|
if (FFooter <> Value) then
|
|
begin
|
|
FFooter := Value;
|
|
Changed(False);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceColumn.SetVertAlign(Value: TVertAlign);
|
|
begin
|
|
if (FVertAlign <> Value) then
|
|
begin
|
|
FVertAlign := Value;
|
|
Changed(False);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceColumn.SetWidth(Value: Integer);
|
|
begin
|
|
if (FWidth <> Value) then
|
|
begin
|
|
FWidth := Value;
|
|
Changed(True);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceColumn.SetVisible(Value: Boolean);
|
|
begin
|
|
if (FVisible <> Value) then
|
|
begin
|
|
FVisible := Value;
|
|
TNiceColumns(Collection).FGrid.ForcedColumn := Index;
|
|
Changed(True);
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceColumn.SetStrings(Value: TStrings);
|
|
begin
|
|
FStrings.Assign(Value);
|
|
Changed(False);
|
|
end;
|
|
|
|
procedure TNiceColumn.FontChange(Sender: TObject);
|
|
begin
|
|
Changed(False);
|
|
end;
|
|
|
|
function TNiceColumn.IsFontStored: Boolean;
|
|
begin
|
|
Result := True;
|
|
with TNiceColumns(Collection).FGrid.Font do
|
|
begin
|
|
if (Charset = FFont.Charset) and
|
|
(Color = FFont.Color) and
|
|
(Height = FFont.Height) and
|
|
(Name = FFont.Name) and
|
|
(Pitch = FFont.Pitch) and
|
|
(PixelsPerInch = FFont.PixelsPerInch) and
|
|
(Size = FFont.Size) and
|
|
(Style = FFont.Style)
|
|
then Result := False;
|
|
end;
|
|
end;
|
|
|
|
function TNiceColumn.GetGrid: TNiceGrid;
|
|
begin
|
|
Result := TNiceColumns(Collection).FGrid;
|
|
end;
|
|
|
|
function TNiceColumn.GetDisplayName: string;
|
|
begin
|
|
if (FTitle <> '')
|
|
then Result := FTitle
|
|
else Result := 'Column ' + IntToStr(Index);
|
|
end;
|
|
|
|
{ TNiceColumns }
|
|
|
|
constructor TNiceColumns.Create(AGrid: TNiceGrid);
|
|
begin
|
|
inherited Create(TNiceColumn);
|
|
FGrid := AGrid;
|
|
end;
|
|
|
|
function TNiceColumns.Add: TNiceColumn;
|
|
begin
|
|
Result := TNiceColumn(inherited Add);
|
|
end;
|
|
|
|
function TNiceColumns.GetItem(Index: Integer): TNiceColumn;
|
|
begin
|
|
Result := TNiceColumn(inherited GetItem(Index));
|
|
end;
|
|
|
|
procedure TNiceColumns.SetItem(Index: Integer; Value: TNiceColumn);
|
|
begin
|
|
inherited SetItem(Index, Value);
|
|
end;
|
|
|
|
function TNiceColumns.GetOwner: TPersistent;
|
|
begin
|
|
Result := FGrid;
|
|
end;
|
|
|
|
function TNiceColumns.Insert(Index: Integer): TNiceColumn;
|
|
begin
|
|
Result := AddItem(nil, Index);
|
|
end;
|
|
|
|
function TNiceColumns.AddItem(Item: TNiceColumn;
|
|
Index: Integer): TNiceColumn;
|
|
begin
|
|
if (Item = nil)
|
|
then Result := FGrid.CreateColumn
|
|
else
|
|
begin
|
|
Result := Item;
|
|
if Assigned(Item) then
|
|
begin
|
|
Result.Collection := Self;
|
|
if (Index < 0)
|
|
then Index := Count - 1;
|
|
Result.Index := Index;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceColumns.Update(Item: TCollectionItem);
|
|
begin
|
|
if (Item <> nil)
|
|
then FGrid.UpdateColumn(Item.Index)
|
|
else FGrid.UpdateColumns;
|
|
end;
|
|
|
|
|
|
{ TAlignedEdit }
|
|
|
|
constructor TNiceInplace.Create(Grid: TNiceGrid);
|
|
begin
|
|
inherited Create(FGrid);
|
|
FGrid := Grid;
|
|
FAlignment := haLeft;
|
|
Parent := FGrid;
|
|
ParentColor := False;
|
|
BorderStyle := bsNone;
|
|
Left := -200;
|
|
Top := -200;
|
|
Visible := False;
|
|
end;
|
|
|
|
procedure TNiceInplace.CreateParams(var Params: TCreateParams);
|
|
const
|
|
Alignments: array [THorzAlign] of Cardinal = (ES_LEFT, ES_CENTER, ES_RIGHT);
|
|
begin
|
|
inherited CreateParams(Params);
|
|
Params.Style := Params.Style or Alignments[FAlignment];
|
|
end;
|
|
|
|
procedure TNiceInplace.SetAlignment(Value: THorzAlign);
|
|
begin
|
|
if (FAlignment <> Value) then
|
|
begin
|
|
FAlignment := Value;
|
|
RecreateWnd{$IFDEF FPC}(Self){$ENDIF};
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceInplace.ShowEdit(X, Y: Integer);
|
|
var
|
|
Rc: TRect;
|
|
Column: TNiceColumn;
|
|
l, t, w, h: Integer;
|
|
begin
|
|
|
|
if CaretVisible
|
|
then HideCaret(Handle);
|
|
CaretVisible := False;
|
|
|
|
CellX := X;
|
|
CellY := Y;
|
|
Column := FGrid.FColumns[x];
|
|
Color := FGrid.GetCellColor(X, Y);
|
|
SetAlignment(Column.FHorzAlign);
|
|
Text := FGrid.SafeGetCell(X, Y);
|
|
Font.Assign(Column.FFont);
|
|
{$IFDEF FPC}
|
|
if Font.Height = 0 then
|
|
Font.Height := GetFontData(Font.Reference.Handle).Height;
|
|
{$ENDIF}
|
|
|
|
Rc := FGrid.GetCellRect(X, Y);
|
|
Rc := FGrid.CellRectToClient(Rc);
|
|
|
|
if (FAlignment = haRight)
|
|
then Rc.Right := Rc.Right + 1;
|
|
InflateRect(Rc, -4, -3);
|
|
|
|
l := Rc.Left;
|
|
w := Rc.Right - Rc.Left;
|
|
t := 0;
|
|
// h := FGrid.TextExtent('gM').CY;
|
|
h := FGrid.Canvas.TextHeight('gM');
|
|
case Column.FVertAlign of
|
|
vaTop: t := Rc.Top - 1;
|
|
vaCenter: t := Rc.Top + (((Rc.Bottom - Rc.Top) - h) div 2);
|
|
vaBottom: t := Rc.Bottom - h + 1;
|
|
end;
|
|
|
|
SetBounds(l, t, w, h);
|
|
Show;
|
|
|
|
end;
|
|
|
|
procedure TNiceInplace.HideEdit;
|
|
begin
|
|
if Visible
|
|
then Hide;
|
|
FGrid.IsEditing := False;
|
|
end;
|
|
|
|
procedure TNiceInplace.Change;
|
|
begin
|
|
inherited;
|
|
FGrid.InternalSetCell(CellX, CellY, Text, True);
|
|
end;
|
|
|
|
procedure TNiceInplace.KeyDown(var Key: Word; Shift: TShiftState);
|
|
begin
|
|
case Key of
|
|
VK_ESCAPE, VK_RETURN, VK_UP, VK_DOWN:
|
|
begin
|
|
HideEdit;
|
|
FGrid.SetFocus;
|
|
end;
|
|
else
|
|
inherited;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceInplace.KeyPress(var Key: Char);
|
|
var
|
|
Allowed: Boolean;
|
|
begin
|
|
Allowed := True;
|
|
if Assigned(FGrid.FOnFilterChar)
|
|
then FGrid.FOnFilterChar(Self, CellX, CellY, Key, Allowed);
|
|
if (not Allowed) and (Key <> Chr(VK_BACK))
|
|
then Key := Chr(0);
|
|
inherited;
|
|
end;
|
|
|
|
{ TNiceGridSync }
|
|
|
|
constructor TNiceGridSync.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FOnDeleteRow := SyncDeleteRow;
|
|
FOnInsertRow := SyncInsertRow;
|
|
FOnColRowChanged := SyncColRow;
|
|
end;
|
|
|
|
procedure TNiceGridSync.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
if (AComponent = FGrid) and (Operation = opRemove)
|
|
then FGrid := nil;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TNiceGridSync.SetGrid(const Value: TNiceGrid);
|
|
begin
|
|
if (FGrid <> Value) then
|
|
begin
|
|
FGrid := Value;
|
|
FGrid.Sync := Self;
|
|
FGrid.RowCount := RowCount;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGridSync.SetScrollBar(AKind, AMax, APos, AMask: Integer);
|
|
begin
|
|
Unused(AMax);
|
|
if (AKind = SB_VERT) and Assigned(FGrid) then
|
|
begin
|
|
if ((AMask and SIF_POS) <> 0)
|
|
then FGrid.VertOffset := APos;
|
|
end;
|
|
end;
|
|
|
|
procedure TNiceGridSync.ShowHideScrollBar(HorzVisible,
|
|
VertVisible: Boolean);
|
|
begin
|
|
Unused(HorzVisible, VertVisible);
|
|
ShowScrollBar(Handle, SB_HORZ, True);
|
|
ShowScrollBar(Handle, SB_VERT, False);
|
|
EnableScrollBar(Handle, SB_HORZ, 3{ESB_DISABLE_BOTH});
|
|
end;
|
|
|
|
procedure TNiceGridSync.SyncColRow(Sender: TObject; ACol, ARow: Integer);
|
|
begin
|
|
Unused(ACol);
|
|
if Assigned(FGrid)
|
|
then FGrid.Row := ARow;
|
|
end;
|
|
|
|
procedure TNiceGridSync.SyncDeleteRow(Sender: TObject; ARow: Integer);
|
|
begin
|
|
if Assigned(FGrid)
|
|
then FGrid.DeleteRow(ARow);
|
|
end;
|
|
|
|
procedure TNiceGridSync.SyncInsertRow(Sender: TObject; ARow: Integer);
|
|
begin
|
|
if Assigned(FGrid) then
|
|
begin
|
|
if (ARow = FGrid.RowCount)
|
|
then FGrid.AddRow
|
|
else FGrid.InsertRow(ARow);
|
|
end;
|
|
end;
|
|
|
|
{ TMergeCell }
|
|
|
|
constructor TMergeCell.Create;
|
|
begin
|
|
inherited Create;
|
|
Font := TFont.Create;
|
|
end;
|
|
|
|
destructor TMergeCell.Destroy;
|
|
begin
|
|
Font.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
end.
|
|
|