home *** CD-ROM | disk | FTP | other *** search
- // This unit will not work with Delphi 1
-
- {*****************************************************************************}
- { }
- { QDBGrid v2.12 Visual Components for Delphi 2, 3, & 4 }
- { }
- { Copyright (c) 1998 Robert R. Marsh, S.J. }
- { & the British Province of the Society of Jesus }
- { }
- { This is very much a first draft of a grid component to work }
- { with QDB. It is, in origin, a standard string grid with some }
- { methods and properties removed and others kept protected or }
- { public rather than published. Borland copyright still covers }
- { this code. I have simply taken the basic string grid and }
- { derived it directly from TCustomControl rather than going via }
- { TCustomGrid and TDrawGrid to eliminate repeated code. I have then }
- { attempted to make the resulting grid QDB-aware. }
- { In the process I have learned from Borland's own TDBGrid and from }
- { Alexander Halser's DataGrid (http://www.easycash.co.at). Many }
- { thanks To Alex for sharing his expertise. }
- { }
- { This is, I'm afraid, all the documentation available apart from }
- { the comments in the code. Much of the code is basic grid stuff-- }
- { the most important bits of new code are marked by //<> }
- { }
- { At present QDBGrid will display the contents of the file opened }
- { in the assigned QDBItem component. Columns are displayed if the }
- { column title matches a QDBItem field name. The columns can be }
- { named via the Columns[n].Titles property or via the component }
- { editor. If no columns are titled all fields are displayed. }
- { The Columns property allows you to choose font, alignment, color, }
- { popup menu, etc., on a column-by-column basis. ]
- { Rows can be deleted (ctrl-del) or added (ctrl-ins). An added row }
- { is inserted above current row. If focus shifts away from the new }
- { row before data has been added the new row is removed. }
- { }
- { The grid tries to display cells according to their contents as }
- { given by the Coilumns.FieldType property. If MatchRowHeightToFont }
- { is set then rows are sized to fit the font of the largest column. ]
- { When DisplayThumbnails is true graphic fields are displayed to }
- { fit the cell. Boolean fields are shown with check marks. Memo }
- { fields (ftstrings) just show the start of the field. You can also }
- { set a DisplayMask and an EditMask for each field. }
- { }
- { How fields are edited also depends upon their type. If the ]
- [ ButtonStyle property of column is set to cbsButton a button is ]
- { shown which triggers the OnEditButtonClick event. A value of }
- { cbsAuto checks the field type of the column: if a Picklist has }
- { been assigned to the column it is used in editing, otherwise memos, }
- { graphics, and boolean values are given a button which launches a }
- { custom editor.
- { }
- { I have attempted to make QDBGrid cope with large files even though }
- { it allocates a row for each item in a file. The sparse array used }
- { to store the cells contents helps minimize the problem and QDBGrid }
- { is careful only to load the fields of rows that are actually }
- { visible. }
- { }
- { Right-clicking on the grid at design-time (but only when a valid }
- { QDBItem is attached with an open file) gives access to two }
- { component editors. One loads all the fields from the file into the }
- { grid columns. The other lets you change the many properties of the }
- { individual columns by right-click access to popup menus. The columns }
- { can, of course, also be edited via the object inspector. }
- { }
- { Please feel free to play with TQDBGrid and make changes. Let }
- { me know what you change and I'll incorporate whatever seems to }
- { fit well. My time is severely limited at the moment so I don't }
- { expect to make much progress myself for a while. Over to you! }
- { }
- { QDBGrid is supplied as is. The author disclaims all warranties, }
- { expressed or implied, including, without limitation, the }
- { warranties of merchantability and of fitness for any purpose. }
- { The author assumes no liability for damages, direct or }
- { consequential, which may result from the use of QDBGrid. }
- { }
- { rrm@sprynet.com }
- { http://home.sprynet.com/sprynet/rrm }
- { }
- {*****************************************************************************}
-
-
- {*******************************************************}
- { }
- { Delphi Visual Component Library }
- { }
- { Copyright (c) 1995,96 Borland International }
- { }
- {*******************************************************}
-
- unit QDBGrids;
-
- {$R-}
-
- interface
-
- uses SysUtils, Messages, Windows, Classes, Graphics, Menus, Controls, Forms,
- StdCtrls, Mask, QDBView;
-
- const
- MaxCustomExtents = MaxListSize;
- MaxShortInt = High(ShortInt);
-
- type
- EInvalidGridOperation = class(Exception);
-
- { Internal grid types }
- TGetExtentsFunc = function(Index: Longint): Integer of object;
-
- TGridAxisDrawInfo = record
- EffectiveLineWidth: Integer;
- FixedBoundary: Integer;
- GridBoundary: Integer;
- GridExtent: Integer;
- LastFullVisibleCell: Longint;
- FullVisBoundary: Integer;
- FixedCellCount: Integer;
- FirstGridCell: Integer;
- GridCellCount: Integer;
- GetExtent: TGetExtentsFunc;
- end;
-
- TGridDrawInfo = record
- Horz, Vert: TGridAxisDrawInfo;
- end;
-
- TGridState = (gsNormal, gsSelecting, gsRowSizing, gsColSizing, gsRowMoving, gsColMoving);
-
- { TQDBGridInplaceEdit }
-
- TQDBGrid = class;
-
- TEditStyle = (esSimple, esButton, esPickList, esMemo, esGraphic, esBoolean);
-
- TPopupListbox = class;
-
- TQDBGridInplaceEdit = class(TCustomMaskEdit)
- private
- FButtonWidth: Integer;
- FClickTime: Longint;
- FEditStyle: TEditStyle;
- FGrid: TQDBGrid;
- FListVisible: Boolean;
- FPickList: TPopupListbox;
- FPressed: Boolean;
- FTracking: Boolean;
- procedure InternalMove(const Loc: TRect; Redraw: Boolean);
- procedure SetGrid(Value: TQDBGrid);
- procedure CMShowingChanged(var Message: TMessage); message CM_SHOWINGCHANGED;
- procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
- procedure WMPaste(var Message); message WM_PASTE;
- procedure WMCut(var Message); message WM_CUT;
- procedure WMClear(var Message); message WM_CLEAR;
- procedure ListMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- procedure SetEditStyle(Value: TEditStyle);
- procedure StopTracking;
- procedure TrackButton(X, Y: Integer);
- procedure CMCancelMode(var Message: TCMCancelMode); message CM_CancelMode;
- procedure WMCancelMode(var Message: TMessage); message WM_CancelMode;
- procedure WMKillFocus(var Message: TMessage); message WM_KillFocus;
- procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message wm_LButtonDblClk;
- procedure WMPaint(var Message: TWMPaint); message wm_Paint;
- procedure WMSetCursor(var Message: TWMSetCursor); message WM_SetCursor;
- protected
- procedure CreateParams(var Params: TCreateParams); override;
- procedure DblClick; override;
- function EditCanModify: Boolean; override;
- procedure KeyDown(var Key: Word; Shift: TShiftState); override;
- procedure KeyPress(var Key: Char); override;
- procedure KeyUp(var Key: Word; Shift: TShiftState); override;
- procedure BoundsChanged; virtual;
- procedure UpdateContents; virtual;
- procedure WndProc(var Message: TMessage); override;
- property Grid: TQDBGrid read FGrid;
- procedure CloseUp(Accept: Boolean);
- procedure DoDropDownKeys(var Key: Word; Shift: TShiftState);
- procedure DropDown;
- 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 PaintWindow(DC: HDC); override;
- property EditStyle: TEditStyle read FEditStyle write SetEditStyle;
- property PickList: TPopupListbox read FPickList;
- public
- constructor Create(AOwner: TComponent); override;
- procedure Deselect;
- procedure Hide;
- procedure Invalidate; override;
- procedure Move(const Loc: TRect);
- function PosEqual(const Rect: TRect): Boolean;
- procedure SetFocus; override;
- procedure UpdateLoc(const Loc: TRect);
- function Visible: Boolean;
- end;
-
- { TPopupListbox }
-
- TPopupListbox = class(TCustomListbox)
- private
- FSearchText: string;
- FSearchTickCount: Longint;
- protected
- procedure CreateParams(var Params: TCreateParams); override;
- procedure CreateWnd; override;
- procedure KeyPress(var Key: Char); override;
- procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
- end;
-
-
- TGridOption = (goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine,
- goDrawFocusSelected, goRowSizing, goColSizing,
- goEditing, goTabs, goSelectColumns, goAlwaysShowEditor,
- goThumbTracking, goAllowDelete, goAllowAdd);
- TGridOptions = set of TGridOption;
- TGridDrawState = set of (gdSelected, gdFocused, gdFixed);
- TGridScrollDirection = set of (sdLeft, sdRight, sdUp, sdDown);
-
- TGridCoord = record
- X: Longint;
- Y: Longint;
- end;
-
- TGridRect = record
- case Integer of
- 0: (Left, Top, Right, Bottom: Longint);
- 1: (TopLeft, BottomRight: TGridCoord);
- end;
-
- TSelectCellEvent = procedure(Sender: TObject; Col, Row: Longint; var CanSelect: Boolean) of object;
- TDrawCellEvent = procedure(Sender: TObject; Col, Row: Longint; Rect: TRect; State: TGridDrawState) of object;
- THeaderClickEvent = procedure(Sender: TObject; Col: Longint) of object;
-
- TGetEditEvent = procedure(Sender: TObject; ACol, ARow: Longint; var Value: string) of object;
- TSetEditEvent = procedure(Sender: TObject; ACol, ARow: Longint; const Value: string) of object;
- TMovedEvent = procedure(Sender: TObject; FromIndex, ToIndex: Longint) of object;
-
- { TQDBGrid }
-
- TColumnValue = (cvColor, cvFont, cvAlignment, cvReadOnly, cvTitleAlignment, cvTitleFont);
- TColumnValues = set of TColumnValue;
- TColumnButtonStyle = (cbsAuto, cbsButton, cbsNone);
- TButtonClickEvent = procedure(Sender: TObject; var text: string) of object;
-
- TColumn = class(TCollectionItem)
- private
- FAlignment: TAlignment;
- FAssignedValues: TColumnValues;
- FButtonStyle: TColumnButtonStyle;
- FColor: TColor;
- FDisplayMask: string;
- FDropDownRows: Integer;
- FEditMask: string;
- FFieldIndex: integer;
- FFieldType: TQDBFieldType;
- FFont: TFont;
- FLimitToList: Boolean;
- FPickList: TStrings;
- FPopupMenu: TPopupMenu;
- FReadonly: Boolean;
- FSelected: boolean;
- FTitle: string;
- FTitleAlignment: TAlignment;
- FTitleFont: TFont;
- FOnButtonClick: TButtonClickEvent;
- FOnInvalidValue: TNotifyEvent;
- procedure FontChanged(Sender: TObject);
- function GetAlignment: TAlignment;
- function GetColor: TColor;
- function GetDisplayMask: string;
- function GetFont: TFont;
- function GetPickList: TStrings;
- function GetReadOnly: Boolean;
- function GetTitleAlignment: TAlignment;
- function GetTitleFont: TFont;
- function IsAlignmentStored: boolean;
- function IsColorStored: Boolean;
- function IsDisplayMaskStored: Boolean;
- function IsFontStored: Boolean;
- function IsReadOnlyStored: Boolean;
- function IsTitleAlignmentStored: boolean;
- function IsTitleFontStored: Boolean;
- procedure SetAlignment(value: TAlignment);
- procedure SetButtonStyle(Value: TColumnButtonStyle);
- procedure SetColor(Value: TColor);
- procedure SetDisplayMask(Value: string); virtual;
- procedure SetFont(Value: TFont);
- procedure SetPickList(Value: TStrings);
- procedure SetPopupMenu(Value: TPopupMenu);
- procedure SetReadOnly(Value: Boolean); virtual;
- procedure SetTitle(Value: string);
- procedure SetTitleAlignment(value: TAlignment);
- procedure SetTitleFont(Value: TFont);
- procedure TitleFontChanged(Sender: TObject);
- protected
- function GetGrid: TQDBGrid;
- procedure RefreshDefaultFont;
- public
- constructor Create(Collection: TCollection); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- function DefaultAlignment: TAlignment;
- function DefaultColor: TColor;
- function DefaultFont: TFont;
- function DefaultReadOnly: Boolean;
- function DefaultTitleAlignment: TAlignment;
- procedure RestoreDefaults; virtual;
- property AssignedValues: TColumnValues read FAssignedValues;
- property FieldIndex: integer read FFieldIndex;
- property FieldType: TQDBFieldType read FFieldType write FFieldType;
- property Grid: TQDBGrid read GetGrid;
- property Selected: boolean read FSelected write FSelected;
- published
- property Alignment: TAlignment read GetAlignment write SetAlignment stored IsAlignmentStored;
- property ButtonStyle: TColumnButtonStyle read FButtonStyle write SetButtonStyle default cbsAuto;
- property Color: TColor read GetColor write SetColor stored IsColorStored;
- property DisplayMask: string read GetDisplayMask write SetDisplayMask stored IsDisplayMaskStored;
- property DropDownRows: Integer read FDropDownRows write FDropDownRows default 7;
- property EditMask: string read FEditMask write FEditMask;
- property Font: TFont read GetFont write SetFont stored IsFontStored;
- property LimitToList: Boolean read FLimitToList write FLimitToList default false;
- property PickList: TStrings read GetPickList write SetPickList;
- property PopupMenu: TPopupMenu read FPopupMenu write SetPopupMenu;
- property ReadOnly: Boolean read GetReadOnly write SetReadOnly stored IsReadOnlyStored;
- property Title: string read FTitle write SetTitle;
- property TitleAlignment: TAlignment read GetTitleAlignment write SetTitleAlignment stored IsTitleAlignmentStored;
- property TitleFont: TFont read GetTitleFont write SetTitleFont stored IsTitleFontStored;
- property OnButtonClick: TButtonClickEvent read FOnButtonClick write FOnButtonClick;
- property OnInvalidValue: TNotifyEvent read FOnInvalidValue write FOnInvalidValue;
- end;
-
- TColumnClass = class of TColumn;
-
- TQDBGridColumns = class(TCollection)
- private
- FGrid: TQDBGrid;
- function GetColumn(Index: Integer): TColumn;
- procedure SetColumn(Index: Integer; Value: TColumn);
- protected
- procedure AddFive;
- {$IFNDEF Ver90}
- function GetOwner: TPersistent; override; {D3}
- {$ENDIF}
- procedure Update(Item: TCollectionItem); override;
- public
- constructor Create(Grid: TQDBGrid; ColumnClass: TColumnClass);
- function Add: TColumn;
- procedure RestoreDefaults;
- property Grid: TQDBGrid read FGrid;
- property Items[Index: Integer]: TColumn read GetColumn write SetColumn; default;
- end;
-
- TQDBGridStrings = class(TStrings)
- private
- FGrid: TQDBGrid;
- FIndex: Integer;
- procedure CalcXY(Index: Integer; var X, Y: Integer);
- protected
- procedure Clear; override;
- function Add(const S: string): Integer; override;
- function Get(Index: Integer): string; override;
- function GetCount: Integer; override;
- procedure Put(Index: Integer; const S: string); override;
- procedure SetUpdateState(Updating: Boolean); override;
- public
- constructor Create(AGrid: TQDBGrid; AIndex: Longint);
- procedure Assign(Source: TPersistent); override;
- end;
-
- { descends directly from TCustomControl -- effectively }
- { combining TCustomGrid, TDrawGrid, and TStringGrid }
- TQDBGrid = class(TCustomControl)
- private
- busy: boolean;
- FAdding: boolean; //<> true when a row has been added but not yet stored }
- FAnchor: TGridCoord;
- FBorderStyle: TBorderStyle;
- FCanEditModify: Boolean;
- FColCount: Longint;
- FColumns: TQDBGridColumns; //<>
- FColWidths: Pointer;
- FDisplayThumbnails: boolean; //<>
- FTabStops: Pointer;
- FCurrent: TGridCoord;
- FDefaultColWidth: Integer;
- FDefaultRowHeight: Integer;
- FFixedColor: TColor;
- FFixedCols: integer; //<> truly fixed at 1 !
- FFixedRows: integer; //<> truly fixed at 1 !
- FGridLineWidth: Integer;
- FMatchRowHeightToFont: boolean; //<>
- FOptions: TGridOptions;
- FOriginalText: string; //<>
- FRowCount: Longint;
- FRowHeights: Pointer;
- FScrollBars: TScrollStyle;
- FSizingIndex: Longint;
- FSizingPos, FSizingOfs: Integer;
- FTitleFont: TFont;
- FTopLeft: TGridCoord;
- FMoveIndex, FMovePos: Longint;
- FHitTest: TPoint;
- FInplaceEdit: TQDBGridInplaceEdit;
- FInplaceCol, FInplaceRow: Longint;
- FEditorMode: Boolean;
- FColOffset: Integer;
- FBeforeInsert: TNotifyEvent;
- FBeforeDelete: TNotifyEvent;
- FOnColumnMoved: TMovedEvent;
- FOnDrawCell: TDrawCellEvent;
- FOnEditButtonClick: TNotifyEvent;
- FOnGetEditMask: TGetEditEvent;
- FOnGetEditText: TGetEditEvent;
- FOnHeaderClick: THeaderClickEvent;
- FOnSelectCell: TSelectCellEvent;
- FOnSetEditText: TSetEditEvent;
- FOnTopLeftChanged: TNotifyEvent;
- FData: Pointer;
- FUpdating: Boolean;
- FNeedsUpdating: Boolean;
- FEditUpdate: Integer;
- FQDBItem: TQDBItem; //<> the associated QDB data source
- FGridState: TGridState;
- FSaveCellExtents: Boolean;
- function CalcCoordFromPoint(X, Y: Integer; const DrawInfo: TGridDrawInfo): TGridCoord;
- procedure CalcDrawInfo(var DrawInfo: TGridDrawInfo);
- procedure CalcDrawInfoXY(var DrawInfo: TGridDrawInfo; UseWidth, UseHeight: Integer);
- procedure CalcFixedInfo(var DrawInfo: TGridDrawInfo);
- function CalcMaxTopLeft(const Coord: TGridCoord; const DrawInfo: TGridDrawInfo): TGridCoord;
- procedure CalcSizingState(X, Y: Integer; var State: TGridState; var Index: Longint; var SizingPos, SizingOfs: Integer; var FixedInfo: TGridDrawInfo);
- procedure ChangeSize(NewColCount, NewRowCount: Longint);
- procedure ClampInView(const Coord: TGridCoord);
- procedure DefaultHandler(var Msg); override;
- procedure DisableEditUpdate;
- procedure DrawSizingLine(const DrawInfo: TGridDrawInfo);
- procedure DrawMove;
- procedure EnableEditUpdate;
- function EnsureDataRow(ARow: Integer): Pointer;
- procedure FocusCell(ACol, ARow: Longint; MoveAnchor: Boolean);
- function GetCells(ACol, ARow: Integer): string;
- function GetColWidths(Index: Longint): Integer;
- function GetQDBItem: TQDBItem; //<>
- function GetRowHeights(Index: Longint): Integer;
- function GetSelection: TGridRect;
- function GetTabStops(Index: Longint): Boolean;
- function GetVisibleColCount: Integer;
- function GetVisibleRowCount: Integer;
- procedure GridRectToScreenRect(GridRect: TGridRect; var ScreenRect: TRect; IncludeLine: Boolean);
- procedure HideEdit;
- procedure Initialize;
- procedure InvalidateGrid;
- procedure InvalidateRect(ARect: TGridRect);
- procedure ModifyScrollBar(ScrollBar, ScrollCode, Pos: Cardinal);
- procedure MoveAdjust(var CellPos: Longint; FromIndex, ToIndex: Longint);
- procedure MoveAnchor(const NewAnchor: TGridCoord);
- procedure MoveAndScroll(Mouse, CellHit: Integer; var DrawInfo: TGridDrawInfo; var Axis: TGridAxisDrawInfo; Scrollbar: Integer);
- procedure MoveCurrent(ACol, ARow: Longint; MoveAnchor, Show: Boolean);
- procedure MoveTopLeft(ALeft, ATop: Longint);
- procedure ReadColCount(Reader: TReader);
- procedure ReadColWidths(Reader: TReader);
- procedure ReadRowHeights(Reader: TReader);
- procedure ResizeCol(Index: Longint; OldSize, NewSize: Integer);
- procedure ResizeRow(Index: Longint; OldSize, NewSize: Integer);
- procedure ScrollDataInfo(DX, DY: Integer; var DrawInfo: TGridDrawInfo);
- procedure SelectionMoved(const OldSel: TGridRect);
- procedure SetBorderStyle(Value: TBorderStyle);
- procedure SetCells(ACol, ARow: Integer; const Value: string);
- procedure SetCol(Value: Longint);
- procedure SetColCount(Value: Longint);
- procedure SetColumns(Value: TQDBGridColumns);
- procedure SetColWidths(Index: Longint; Value: Integer);
- procedure SetDefaultColWidth(Value: Integer);
- procedure SetDefaultRowHeight(Value: Integer);
- procedure SetEditorMode(Value: Boolean);
- procedure SetFixedColor(Value: TColor);
- procedure SetGridLineWidth(Value: Integer);
- procedure SetLeftCol(Value: Longint);
- procedure SetOptions(Value: TGridOptions);
- procedure SetQDBItem(Value: TQDBItem); //<>
- procedure SetRow(Value: Longint);
- procedure SetRowCount(Value: Longint);
- procedure SetRowHeights(Index: Longint; Value: Integer);
- procedure SetScrollBars(Value: TScrollStyle);
- procedure SetSelection(Value: TGridRect);
- procedure SetTabStops(Index: Longint; Value: Boolean);
- procedure SetTitleFont(Value: TFont);
- procedure SetTopRow(Value: Longint);
- procedure SetUpdateState(Updating: Boolean);
- procedure TitleFontChanged(Sender: TObject);
- procedure TopLeftMoved(const OldTopLeft: TGridCoord);
- procedure UpdateCell(ACol, ARow: Integer);
- procedure UpdateEdit;
- procedure UpdateScrollPos;
- procedure UpdateScrollRange;
- procedure UpdateText;
- procedure WriteColCount(Writer: TWriter);
- procedure WriteColWidths(Writer: TWriter);
- procedure WriteRowHeights(Writer: TWriter);
- procedure CMCancelMode(var Msg: TMessage); message CM_CANCELMODE;
- procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
- procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED;
- procedure CMDesignHitTest(var Msg: TCMDesignHitTest); message CM_DESIGNHITTEST;
- procedure CMWantSpecialKey(var Msg: TCMWantSpecialKey); message CM_WANTSPECIALKEY;
- procedure WMChar(var Msg: TWMChar); message WM_CHAR;
- procedure WMCommand(var Message: TWMCommand); message WM_COMMAND;
- procedure WMGetDlgCode(var Msg: TWMGetDlgCode); message WM_GETDLGCODE;
- procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL;
- procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS;
- procedure WMLButtonDown(var Message: TMessage); message WM_LBUTTONDOWN;
- procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHITTEST;
- procedure WMSetCursor(var Msg: TWMSetCursor); message WM_SETCURSOR;
- procedure WMSetFocus(var Msg: TWMSetFocus); message WM_SETFOCUS;
- procedure WMSize(var Msg: TWMSize); message WM_SIZE;
- procedure WMTimer(var Msg: TWMTimer); message WM_TIMER;
- procedure WMVScroll(var Msg: TWMVScroll); message WM_VSCROLL;
- property DefaultRowHeight: Integer read FDefaultRowHeight write SetDefaultRowHeight default 24;
- property RowCount: Longint read FRowCount write SetRowCount;
- protected
- procedure AdjustSize(Index, Amount: Longint; Rows: Boolean); dynamic;
- function BoxRect(ALeft, ATop, ARight, ABottom: Longint): TRect;
- function CanEditAcceptKey(Key: Char): Boolean; dynamic;
- function CanEditModify: Boolean; dynamic;
- function CanEditShow: Boolean; virtual;
- function CanGridAcceptKey(Key: Word; Shift: TShiftState): Boolean; dynamic;
- function CellRect(ACol, ARow: Longint): TRect;
- procedure ColumnMoved(FromIndex, ToIndex: Longint); dynamic;
- procedure ColWidthsChanged; dynamic;
- function CreateColumns: TQDBGridColumns; dynamic;
- function CreateEditor: TQDBGridInplaceEdit; virtual;
- procedure CreateParams(var Params: TCreateParams); override;
- procedure DefineProperties(Filer: TFiler); override;
- procedure DeleteColumn(ACol: Longint);
- procedure DoExit; override;
- procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); virtual;
- procedure EditButtonClick; //<>
- procedure MemoButtonClick; //<>
- procedure GraphicButtonClick; //<>
- procedure BooleanButtonClick; //<>
- procedure FileAssigned(Sender: TObject); //<> responds to QDB.OnFileAssigned
- function GetEditLimit: Integer; dynamic;
- function GetEditMask(ACol, ARow: Longint): string; dynamic;
- function GetEditText(ACol, ARow: Longint): string; dynamic;
- function GetGridHeight: Integer;
- function GetGridWidth: Integer;
- procedure HideEditor;
- procedure InvalidateCell(ACol, ARow: Longint);
- procedure InvalidateCol(ACol: Longint);
- procedure InvalidateEditor;
- procedure InvalidateRow(ARow: Longint);
- procedure KeyDown(var Key: Word; Shift: TShiftState); override;
- procedure KeyPress(var Key: Char); override;
- procedure Load; //<> initial load of data
- procedure Loaded; override;
- procedure LoadFieldStructure; //<> initial load of fields
- procedure LoadRow(ARow: longint); //<> load one row
- procedure MoveColRow(ACol, ARow: Longint; MoveAnchor, Show: Boolean);
- function MouseCoord(X, Y: Integer): TGridCoord;
- 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 MoveColumn(FromIndex, ToIndex: Longint);
- procedure MoveRow(FromIndex, ToIndex: Longint);
- procedure Paint; override;
- procedure PopupMemo;
- procedure PopupGraphic;
- procedure ReLoad; //<> reload visible data only
- procedure RowHeightsChanged; dynamic;
- procedure RowMoved(FromIndex, ToIndex: Longint); dynamic;
- procedure SaveCell(ACol, ARow: longint); //<> store a cell's data
- procedure SaveRow(ARow: longint); //<> store a row's data
- procedure ScrollData(DX, DY: Integer);
- function SelectCell(ACol, ARow: Longint): Boolean; virtual;
- procedure SetColumnCount(NewCount: LongInt);
- procedure SetEditText(ACol, ARow: Longint; const Value: string); dynamic;
- procedure ShowEditor;
- procedure ShowEditorChar(Ch: Char);
- function Sizing(X, Y: Integer): Boolean;
- procedure TimedScroll(Direction: TGridScrollDirection); dynamic;
- procedure TopLeftChanged; dynamic;
- procedure UpdateDesigner;
- property Adding: boolean read FAdding write FAdding;
- property EditorMode: Boolean read FEditorMode write SetEditorMode;
- property GridHeight: Integer read GetGridHeight;
- property GridLineWidth: Integer read FGridLineWidth write SetGridLineWidth default 1;
- property GridWidth: Integer read GetGridWidth;
- property InplaceEditor: TQDBGridInplaceEdit read FInplaceEdit;
- property LeftCol: Longint read FTopLeft.X write SetLeftCol;
- property RowHeights[Index: Longint]: Integer read GetRowHeights write SetRowHeights;
- property Selection: TGridRect read GetSelection write SetSelection;
- property TabStops[Index: Longint]: Boolean read GetTabStops write SetTabStops;
- property TopRow: Longint read FTopLeft.Y write SetTopRow;
- property VisibleColCount: Integer read GetVisibleColCount;
- property VisibleRowCount: Integer read GetVisibleRowCount;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure AddARow; //<> triggered by ctrl-ins
- procedure DeleteARow(ARow: integer); //<> triggered by ctrl-del
- procedure MouseToCell(X, Y: Integer; var ACol, ARow: Longint);
- property Cells[ACol, ARow: Integer]: string read GetCells write SetCells;
- property Col: Longint read FCurrent.X write SetCol;
- property ColCount: Longint read FColCount write SetColCount;
- property ColWidths[Index: Longint]: Integer read GetColWidths write SetColWidths;
- property FixedCols: integer read FFixedCols; { constant }
- property FixedRows: integer read FFixedRows; { constant }
- property Row: Longint read FCurrent.Y write SetRow;
- published
- property Align;
- property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle default bsSingle;
- property Color default clWindow;
- property Columns: TQDBGridColumns read FColumns write SetColumns;
- property Ctl3D;
- property DefaultColWidth: Integer read FDefaultColWidth write SetDefaultColWidth default 64;
- property DisplayThumbnails: boolean read FDisplayThumbnails write FDisplayThumbnails;
- property DragCursor;
- property DragMode;
- property Enabled;
- property FixedColor: TColor read FFixedColor write SetFixedColor default clBtnFace;
- property Font;
- property MatchRowHeightToFont: boolean read FMatchRowHeightToFont write FMatchRowHeightToFont;
- property Options: TGridOptions read FOptions write SetOptions default [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine{, goRangeSelect}];
- property ParentColor default False;
- property ParentCtl3D;
- property ParentFont;
- property ParentShowHint;
- property PopupMenu;
- property QDBItem: TQDBItem read GetQDBItem write SetQDBItem;
- property ScrollBars: TScrollStyle read FScrollBars write SetScrollBars default ssBoth;
- property ShowHint;
- property TabOrder;
- property TabStop;
- property TitleFont: TFont read FTitleFont write SetTitleFont;
- property Visible;
- property BeforeInsert: TNotifyEvent read FBeforeInsert write FBeforeInsert;
- property BeforeDelete: TNotifyEvent read FBeforeDelete write FBeforeDelete;
- property OnClick;
- property OnColumnMoved: TMovedEvent read FOnColumnMoved write FOnColumnMoved;
- property OnDblClick;
- property OnDragDrop;
- property OnDragOver;
- property OnDrawCell: TDrawCellEvent read FOnDrawCell write FOnDrawCell;
- property OnEditButtonClick: TNotifyEvent read FOnEditButtonClick write FOnEditButtonClick;
- property OnEndDrag;
- property OnEnter;
- property OnExit;
- property OnGetEditMask: TGetEditEvent read FOnGetEditMask write FOnGetEditMask;
- property OnGetEditText: TGetEditEvent read FOnGetEditText write FOnGetEditText;
- property OnHeaderClick: THeaderClickEvent read FOnHeaderClick write FOnHeaderClick;
- property OnKeyDown;
- property OnKeyPress;
- property OnKeyUp;
- property OnMouseDown;
- property OnMouseMove;
- property OnMouseUp;
- property OnSelectCell: TSelectCellEvent read FOnSelectCell write FOnSelectCell;
- property OnSetEditText: TSetEditEvent read FOnSetEditText write FOnSetEditText;
- property OnStartDrag;
- property OnTopLeftChanged: TNotifyEvent read FOnTopLeftChanged write FOnTopLeftChanged;
- end;
-
- procedure KillMessage(Wnd: HWnd; Msg: Integer);
-
- implementation
-
- uses
- Consts, Sparse, MemoUnit, GraphicUnit;
-
- type
- PIntArray = ^TIntArray;
- TIntArray = array[0..MaxCustomExtents] of Integer;
-
- const
- SIndexOutOfRange = 'SIndexOutOfRange';
- STooManyDeleted = 'STooManyDeleted';
- SGridTooLarge = 'SGridTooLarge';
-
- const
- FixedColWidth = 15;
- FontHeightMargin = 7;
-
- procedure InvalidOp(msg: string);
- begin
- raise EInvalidGridOperation.Create(msg);
- end;
-
- procedure KillMessage(Wnd: HWnd; Msg: Integer);
- // Delete the requested message from the queue, but throw back
- // any WM_QUIT msgs so that PeekMessage can return correctly
- var
- M: TMsg;
- begin
- M.Message := 0;
- if PeekMessage(M, Wnd, Msg, Msg, pm_Remove) and (M.Message = WM_QUIT) then
- PostQuitMessage(M.wparam);
- end;
-
- function IMin(A, B: Integer): Integer;
- begin
- Result := B;
- if A < B then Result := A;
- end;
-
- function IMax(A, B: Integer): Integer;
- begin
- Result := B;
- if A > B then Result := A;
- end;
-
- function CoordInRect(const ACoord: TGridCoord; const ARect: TGridRect): Boolean;
- begin
- with ACoord, ARect do
- Result := (X >= Left) and (X <= Right) and (Y >= Top) and (Y <= Bottom);
- end;
-
- function GridRect(Coord1, Coord2: TGridCoord): TGridRect;
- begin
- with Result do
- begin
- Left := Coord2.X;
- if Coord1.X < Coord2.X then Left := Coord1.X;
- Right := Coord1.X;
- if Coord1.X < Coord2.X then Right := Coord2.X;
- Top := Coord2.Y;
- if Coord1.Y < Coord2.Y then Top := Coord1.Y;
- Bottom := Coord1.Y;
- if Coord1.Y < Coord2.Y then Bottom := Coord2.Y;
- end;
- end;
-
- function GridRectUnion(const ARect1, ARect2: TGridRect): TGridRect;
- begin
- with Result do
- begin
- Left := ARect1.Left;
- if ARect1.Left > ARect2.Left then Left := ARect2.Left;
- Right := ARect1.Right;
- if ARect1.Right < ARect2.Right then Right := ARect2.Right;
- Top := ARect1.Top;
- if ARect1.Top > ARect2.Top then Top := ARect2.Top;
- Bottom := ARect1.Bottom;
- if ARect1.Bottom < ARect2.Bottom then Bottom := ARect2.Bottom;
- end;
- end;
-
- function PointInGridRect(Col, Row: Longint; const Rect: TGridRect): Boolean;
- begin
- Result := (Col >= Rect.Left) and (Col <= Rect.Right) and (Row >= Rect.Top)
- and (Row <= Rect.Bottom);
- end;
-
- type
- TXorRects = array[0..3] of TRect;
-
- procedure XorRects(const R1, R2: TRect; var XorRects: TXorRects);
- var
- Intersect, Union: TRect;
-
- function PtInRect(X, Y: Integer; const Rect: TRect): Boolean;
- begin
- with Rect do Result := (X >= Left) and (X <= Right) and (Y >= Top) and
- (Y <= Bottom);
- end;
-
- function Includes(const P1: TPoint; var P2: TPoint): Boolean;
- begin
- with P1 do
- begin
- Result := PtInRect(X, Y, R1) or PtInRect(X, Y, R2);
- if Result then P2 := P1;
- end;
- end;
-
- function Build(var R: TRect; const P1, P2, P3: TPoint): Boolean;
- begin
- Build := True;
- with R do
- if Includes(P1, TopLeft) then
- begin
- if not Includes(P3, BottomRight) then BottomRight := P2;
- end
- else if Includes(P2, TopLeft) then BottomRight := P3
- else Build := False;
- end;
-
- begin
- FillChar(XorRects, SizeOf(XorRects), 0);
- if not Bool(IntersectRect(Intersect, R1, R2)) then
- begin
- { Don't intersect so its simple }
- XorRects[0] := R1;
- XorRects[1] := R2;
- end
- else
- begin
- UnionRect(Union, R1, R2);
- if Build(XorRects[0],
- Point(Union.Left, Union.Top),
- Point(Union.Left, Intersect.Top),
- Point(Union.Left, Intersect.Bottom)) then
- XorRects[0].Right := Intersect.Left;
- if Build(XorRects[1],
- Point(Intersect.Left, Union.Top),
- Point(Intersect.Right, Union.Top),
- Point(Union.Right, Union.Top)) then
- XorRects[1].Bottom := Intersect.Top;
- if Build(XorRects[2],
- Point(Union.Right, Intersect.Top),
- Point(Union.Right, Intersect.Bottom),
- Point(Union.Right, Union.Bottom)) then
- XorRects[2].Left := Intersect.Right;
- if Build(XorRects[3],
- Point(Union.Left, Union.Bottom),
- Point(Intersect.Left, Union.Bottom),
- Point(Intersect.Right, Union.Bottom)) then
- XorRects[3].Top := Intersect.Bottom;
- end;
- end;
-
- procedure ModifyExtents(var Extents: Pointer; Index, Amount: Longint;
- Default: Integer);
- var
- LongSize: LongInt;
- NewSize: Cardinal;
- OldSize: Cardinal;
- I: Cardinal;
- begin
- if Amount <> 0 then
- begin
- if not Assigned(Extents) then OldSize := 0
- else OldSize := PIntArray(Extents)^[0];
- if (Index < 0) or (OldSize < Index) then InvalidOp(SIndexOutOfRange);
- LongSize := OldSize + Amount;
- if LongSize < 0 then InvalidOp(STooManyDeleted)
- else if LongSize >= MaxListSize - 1 then InvalidOp(SGridTooLarge);
- NewSize := Cardinal(LongSize);
- if NewSize > 0 then Inc(NewSize);
- ReallocMem(Extents, NewSize * SizeOf(Integer));
- if Assigned(Extents) then
- begin
- I := Index;
- while I < NewSize do
- begin
- PIntArray(Extents)^[I] := Default;
- Inc(I);
- end;
- PIntArray(Extents)^[0] := NewSize - 1;
- end;
- end;
- end;
-
- procedure UpdateExtents(var Extents: Pointer; NewSize: Longint;
- Default: Integer);
- var
- OldSize: Integer;
- begin
- OldSize := 0;
- if Assigned(Extents) then OldSize := PIntArray(Extents)^[0];
- ModifyExtents(Extents, OldSize, NewSize - OldSize, Default);
- end;
-
- procedure MoveExtent(var Extents: Pointer; FromIndex, ToIndex: Longint);
- var
- Extent: Integer;
- begin
- if Assigned(Extents) then
- begin
- Extent := PIntArray(Extents)^[FromIndex];
- if FromIndex < ToIndex then
- Move(PIntArray(Extents)^[FromIndex + 1], PIntArray(Extents)^[FromIndex],
- (ToIndex - FromIndex) * SizeOf(Integer))
- else if FromIndex > ToIndex then
- Move(PIntArray(Extents)^[ToIndex], PIntArray(Extents)^[ToIndex + 1],
- (FromIndex - ToIndex) * SizeOf(Integer));
- PIntArray(Extents)^[ToIndex] := Extent;
- end;
- end;
-
- function CompareExtents(E1, E2: Pointer): Boolean;
- var
- I: Integer;
- begin
- Result := False;
- if E1 <> nil then
- begin
- if E2 <> nil then
- begin
- for I := 0 to PIntArray(E1)^[0] do
- if PIntArray(E1)^[I] <> PIntArray(E2)^[I] then Exit;
- Result := True;
- end
- end
- else Result := E2 = nil;
- end;
-
- { Private. LongMulDiv multiplys the first two arguments and then
- divides by the third. This is used so that real number
- (floating point) arithmetic is not necessary. This routine saves
- the possible 64-bit value in a temp before doing the divide. Does
- not do error checking like divide by zero. Also assumes that the
- result is in the 32-bit range (Actually 31-bit, since this algorithm
- is for unsigned). }
-
- function LongMulDiv(Mult1, Mult2, Div1: Longint): Longint; stdcall;
- external 'kernel32.dll' name 'MulDiv';
-
- type
- TSelection = record
- StartPos, EndPos: Integer;
- end;
-
- { TColumn }
-
- constructor TColumn.Create(Collection: TCollection);
- var
- Grid: TQDBGrid;
- begin
- Grid := nil;
- if Assigned(Collection) and (Collection is TQDBGridColumns) then Grid := TQDBGridColumns(Collection).Grid;
- inherited Create(Collection);
- FButtonStyle := cbsAuto;
- FFont := TFont.Create;
- FFont.Assign(DefaultFont);
- FFont.OnChange := FontChanged;
- FTitleFont := TFont.Create;
- if Assigned(Grid.TitleFont) then
- FTitleFont.Assign(Grid.TitleFont)
- else
- FTitleFont.Assign(DefaultFont);
- FTitleFont.OnChange := TitleFontChanged;
- FDropDownRows := 7;
- end;
-
- destructor TColumn.Destroy;
- begin
- Grid.ColCount := Grid.Columns.Count - 1;
- FFont.Free;
- FTitleFont.Free;
- FPickList.Free;
- inherited Destroy;
- end;
-
- procedure TColumn.Assign(Source: TPersistent);
- begin
- if Source is TColumn then
- begin
- if Assigned(Collection) then Collection.BeginUpdate;
- try
- RestoreDefaults;
- if cvColor in TColumn(Source).AssignedValues then
- Color := TColumn(Source).Color;
- if cvFont in TColumn(Source).AssignedValues then
- Font := TColumn(Source).Font;
- if cvAlignment in TColumn(Source).AssignedValues then
- Alignment := TColumn(Source).Alignment;
- if cvReadOnly in TColumn(Source).AssignedValues then
- ReadOnly := TColumn(Source).ReadOnly;
- if cvTitleAlignment in TColumn(Source).AssignedValues then
- TitleAlignment := TColumn(Source).TitleAlignment;
- if cvTitleFont in TColumn(Source).AssignedValues then
- TitleFont := TColumn(Source).Font;
- Title := TColumn(Source).Title;
- DropDownRows := TColumn(Source).DropDownRows;
- ButtonStyle := TColumn(Source).ButtonStyle;
- PickList := TColumn(Source).PickList;
- PopupMenu := TColumn(Source).PopupMenu;
- finally
- if Assigned(Collection) then Collection.EndUpdate;
- end;
- end
- else
- inherited Assign(Source);
- end;
-
- function TColumn.DefaultAlignment: TAlignment;
- begin
- Result := taLeftJustify;
- end;
-
- function TColumn.DefaultColor: TColor;
- var
- Grid: TQDBGrid;
- begin
- Grid := GetGrid;
- if Assigned(Grid) then
- Result := Grid.Color
- else
- Result := clWindow;
- end;
-
- function TColumn.DefaultFont: TFont;
- var
- Grid: TQDBGrid;
- begin
- Grid := GetGrid;
- if Assigned(Grid) then
- Result := Grid.Font
- else
- Result := FFont;
- end;
-
- function TColumn.DefaultReadOnly: Boolean;
- begin
- Result := False;
- end;
-
- function TColumn.DefaultTitleAlignment: TAlignment;
- begin
- Result := taCenter;
- end;
-
- procedure TColumn.FontChanged;
- begin
- Include(FAssignedValues, cvFont);
- RefreshDefaultFont;
- Grid.RowHeightsChanged;
- Changed(False);
- end;
-
- function TColumn.GetAlignment: TAlignment;
- begin
- if cvAlignment in FAssignedValues then
- Result := FAlignment
- else
- Result := DefaultAlignment;
- end;
-
- function TColumn.GetColor: TColor;
- begin
- if cvColor in FAssignedValues then
- Result := FColor
- else
- Result := DefaultColor;
- end;
-
- function TColumn.GetDisplayMask: string;
- begin
- Result := FDisplayMask;
- end;
-
- function TColumn.GetFont: TFont;
- var
- Save: TNotifyEvent;
- begin
- if not (cvFont in FAssignedValues) and (FFont.Handle <> DefaultFont.Handle) then
- begin
- Save := FFont.OnChange;
- FFont.OnChange := nil;
- FFont.Assign(DefaultFont);
- FFont.OnChange := Save;
- end;
- Result := FFont;
- end;
-
- function TColumn.GetGrid: TQDBGrid;
- begin
- if Assigned(Collection) and (Collection is TQDBGridColumns) then
- Result := TQDBGridColumns(Collection).Grid
- else
- Result := nil;
- end;
-
- function TColumn.GetPickList: TStrings;
- begin
- if FPickList = nil then
- FPickList := TStringList.Create;
- Result := FPickList;
- end;
-
- function TColumn.GetReadOnly: Boolean;
- begin
- if cvReadOnly in FAssignedValues then
- Result := FReadOnly
- else
- Result := DefaultReadOnly;
- end;
-
- function TColumn.GetTitleAlignment: TAlignment;
- begin
- if cvTitleAlignment in FAssignedValues then
- Result := FTitleAlignment
- else
- Result := DefaultTitleAlignment;
- end;
-
- function TColumn.GetTitleFont: TFont;
- var
- Save: TNotifyEvent;
- begin
- if not (cvTitleFont in FAssignedValues) and (FTitleFont.Handle <> Grid.TitleFont.Handle) then
- begin
- Save := FTitleFont.OnChange;
- FTitleFont.OnChange := nil;
- FTitleFont.Assign(Grid.TitleFont);
- FTitleFont.OnChange := Save;
- end;
- Result := FTitleFont;
- end;
-
- function TColumn.IsAlignmentStored: Boolean;
- begin
- Result := (cvAlignment in FAssignedValues) and (FAlignment <> DefaultAlignment);
- end;
-
- function TColumn.IsColorStored: Boolean;
- begin
- Result := (cvColor in FAssignedValues) and (FColor <> DefaultColor);
- end;
-
- function TColumn.IsDisplayMaskStored: Boolean;
- begin
- Result := true;
- end;
-
- function TColumn.IsFontStored: Boolean;
- begin
- Result := (cvFont in FAssignedValues);
- end;
-
- function TColumn.IsReadOnlyStored: Boolean;
- begin
- Result := (cvReadOnly in FAssignedValues) and (FReadOnly <> DefaultReadOnly);
- end;
-
- function TColumn.IsTitleAlignmentStored: Boolean;
- begin
- Result := (cvTitleAlignment in FAssignedValues) and (FTitleAlignment <> DefaultTitleAlignment);
- end;
-
- function TColumn.IsTitleFontStored: Boolean;
- begin
- Result := (cvTitleFont in FAssignedValues);
- end;
-
- procedure TColumn.RefreshDefaultFont;
- var
- Save: TNotifyEvent;
- begin
- if not (cvFont in FAssignedValues) then
- begin
- Save := FFont.OnChange;
- FFont.OnChange := nil;
- try
- FFont.Assign(DefaultFont);
- finally
- FFont.OnChange := Save;
- end;
- end;
- if not (cvTitleFont in FAssignedValues) then
- begin
- Save := FTitleFont.OnChange;
- FTitleFont.OnChange := nil;
- try
- FTitleFont.Assign(Grid.TitleFont);
- finally
- FTitleFont.OnChange := Save;
- end;
- end;
- end;
-
- procedure TColumn.RestoreDefaults;
- var
- FontAssigned: Boolean;
- TitleFontAssigned: Boolean;
- begin
- FontAssigned := cvFont in FAssignedValues;
- TitleFontAssigned := cvTitleFont in FAssignedValues;
- FAssignedValues := [];
- RefreshDefaultFont;
- FPickList.Free;
- FPickList := nil;
- ButtonStyle := cbsAuto;
- Changed(FontAssigned or TitleFontAssigned);
- end;
-
- procedure TColumn.SetAlignment(Value: TAlignment);
- begin
- if (cvAlignment in FAssignedValues) and (Value = FAlignment) then Exit;
- FAlignment := Value;
- Include(FAssignedValues, cvAlignment);
- Changed(False);
- end;
-
- procedure TColumn.SetButtonStyle(Value: TColumnButtonStyle);
- begin
- if Value = FButtonStyle then Exit;
- FButtonStyle := Value;
- Changed(False);
- end;
-
- procedure TColumn.SetColor(Value: TColor);
- begin
- if (cvColor in FAssignedValues) and (Value = FColor) then Exit;
- FColor := Value;
- Include(FAssignedValues, cvColor);
- Changed(False);
- end;
-
- procedure TColumn.SetDisplayMask(Value: string);
- begin
- FDisplayMask := value;
- Changed(False);
- end;
-
- procedure TColumn.SetFont(Value: TFont);
- begin
- FFont.Assign(Value);
- if (FieldType = ftboolean) and (FFont.Name <> 'Marlett') then
- FFont.Name:='Marlett';
- Include(FAssignedValues, cvFont);
- Changed(False);
- end;
-
- procedure TColumn.SetPickList(Value: TStrings);
- begin
- if Value = nil then
- begin
- FPickList.Free;
- FPickList := nil;
- Exit;
- end;
- PickList.Assign(Value);
- end;
-
- procedure TColumn.SetPopupMenu(Value: TPopupMenu);
- begin
- FPopupMenu := Value;
- if Value <> nil then Value.FreeNotification(GetGrid);
- end;
-
- procedure TColumn.SetReadOnly(Value: Boolean);
- begin
- if (cvReadOnly in FAssignedValues) and (Value = FReadOnly) then Exit;
- FReadOnly := Value;
- Include(FAssignedValues, cvReadOnly);
- Changed(False);
- end;
-
- procedure TColumn.SetTitle(Value: string);
- begin
- FTitle := Value;
- if Assigned(Grid.QDBItem) then
- with Grid.QDBItem do
- begin
- if FieldIndex(FTitle) <> -1 then
- begin
- FFieldIndex := FieldIndex(FTitle);
- FFieldType := FieldTypes[FFieldIndex];
- if FFieldType = ftboolean then
- FFont.Name:='Marlett';
- end
- else
- begin
- FFieldIndex := -1;
- FFieldType := ftunknown;
- end;
- end;
- end;
-
- procedure TColumn.SetTitleAlignment(Value: TAlignment);
- begin
- if (cvTitleAlignment in FAssignedValues) and (Value = FTitleAlignment) then Exit;
- FTitleAlignment := Value;
- Include(FAssignedValues, cvTitleAlignment);
- Changed(False);
- end;
-
- procedure TColumn.SetTitleFont(Value: TFont);
- begin
- FTitleFont.Assign(Value);
- Include(FAssignedValues, cvTitleFont);
- Changed(False);
- end;
-
- procedure TColumn.TitleFontChanged;
- begin
- Include(FAssignedValues, cvTitleFont);
- RefreshDefaultFont;
- Grid.RowHeights[0] := abs(FTitleFont.Height) + FontHeightMargin;
- Changed(False);
- end;
-
- { TQDBGridColumns }
-
- constructor TQDBGridColumns.Create(Grid: TQDBGrid; ColumnClass: TColumnClass);
- begin
- inherited Create(ColumnClass);
- FGrid := Grid;
- end;
-
- function TQDBGridColumns.Add: TColumn;
- begin
- Result := TColumn(inherited Add);
- Grid.ColCount := Grid.Columns.Count;
- end;
-
- procedure TQDBGridColumns.AddFive;
- begin
- inherited Add;
- inherited Add;
- inherited Add;
- inherited Add;
- inherited Add;
- Grid.ColCount := Grid.Columns.Count;
- end;
-
- function TQDBGridColumns.GetColumn(Index: Integer): TColumn;
- begin
- Result := TColumn(inherited Items[Index]);
- end;
-
- {$IFNDEF VER90}
- function TQDBGridColumns.GetOwner: TPersistent;
- begin
- Result := FGrid;
- end;
- {$ENDIF}
-
- procedure TQDBGridColumns.RestoreDefaults;
- var
- I: Integer;
- begin
- BeginUpdate;
- try
- for I := 0 to Count - 1 do
- Items[I].RestoreDefaults;
- finally
- EndUpdate;
- end;
- end;
-
- procedure TQDBGridColumns.SetColumn(Index: Integer; Value: TColumn);
- begin
- Items[Index].Assign(Value);
- end;
-
- procedure TQDBGridColumns.Update(Item: TCollectionItem);
- begin
- if (FGrid = nil) or (csLoading in FGrid.ComponentState) then Exit;
-
- if (csDesigning in FGrid.ComponentState) then FGrid.invalidate
- else FGrid.invalidatecol(FGrid.Col);
- end;
-
- procedure TPopupListBox.CreateParams(var Params: TCreateParams);
- begin
- inherited CreateParams(Params);
- with Params do
- begin
- Style := Style or WS_BORDER;
- ExStyle := WS_EX_TOOLWINDOW;
- WindowClass.Style := CS_SAVEBITS;
- end;
- end;
-
- procedure TPopupListbox.CreateWnd;
- begin
- inherited CreateWnd;
- Windows.SetParent(Handle, 0);
- CallWindowProc(DefWndProc, Handle, wm_SetFocus, 0, 0);
- end;
-
- procedure TPopupListbox.Keypress(var Key: Char);
- var
- TickCount: Integer;
- begin
- case Key of
- #8, #27: FSearchText := '';
- #32..#255:
- begin
- TickCount := GetTickCount;
- if TickCount - FSearchTickCount > 2000 then FSearchText := '';
- FSearchTickCount := TickCount;
- if Length(FSearchText) < 32 then FSearchText := FSearchText + Key;
- SendMessage(Handle, LB_SelectString, WORD(-1), Longint(PChar(FSearchText)));
- Key := #0;
- end;
- end;
- inherited Keypress(Key);
- end;
-
- procedure TPopupListbox.MouseUp(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer);
- begin
- inherited MouseUp(Button, Shift, X, Y);
- TQDBGridInplaceEdit(Owner).CloseUp((X >= 0) and (Y >= 0) and
- (X < Width) and (Y < Height));
- end;
-
- constructor TQDBGridInplaceEdit.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- ParentCtl3D := False;
- Ctl3D := False;
- TabStop := False;
- BorderStyle := bsNone;
- FButtonWidth := GetSystemMetrics(SM_CXVSCROLL);
- FEditStyle := esSimple;
- end;
-
- procedure TQDBGridInplaceEdit.CreateParams(var Params: TCreateParams);
- begin
- inherited CreateParams(Params);
- Params.Style := Params.Style or ES_MULTILINE;
- end;
-
- procedure TQDBGridInplaceEdit.SetGrid(Value: TQDBGrid);
- begin
- FGrid := Value;
- end;
-
- procedure TQDBGridInplaceEdit.CMShowingChanged(var Message: TMessage);
- begin
- { Ignore showing using the Visible property }
- end;
-
- procedure TQDBGridInplaceEdit.WMGetDlgCode(var Message: TWMGetDlgCode);
- begin
- inherited;
- if goTabs in Grid.Options then
- Message.Result := Message.Result or DLGC_WANTTAB;
- end;
-
- procedure TQDBGridInplaceEdit.WMPaste(var Message);
- begin
- if not EditCanModify then Exit;
- inherited
- end;
-
- procedure TQDBGridInplaceEdit.WMClear(var Message);
- begin
- if not EditCanModify then Exit;
- inherited;
- end;
-
- procedure TQDBGridInplaceEdit.WMCut(var Message);
- begin
- if not EditCanModify then Exit;
- inherited;
- end;
-
- procedure TQDBGridInplaceEdit.DblClick;
- begin
- Grid.DblClick;
- end;
-
- function TQDBGridInplaceEdit.EditCanModify: Boolean;
- begin
- Result := Grid.CanEditModify;
- end;
-
- type
- TKeyEvent = procedure(Sender: TObject; var Key: Word; Shift: TShiftState) of object;
-
- procedure TQDBGridInplaceEdit.KeyDown(var Key: Word; Shift: TShiftState);
-
- procedure SendToParent;
- begin
- Grid.KeyDown(Key, Shift);
- Key := 0;
- end;
-
- procedure ParentEvent;
- var
- GridKeyDown: TKeyEvent;
- begin
- GridKeyDown := Grid.OnKeyDown;
- if Assigned(GridKeyDown) then GridKeyDown(Grid, Key, Shift);
- end;
-
- function ForwardMovement: Boolean;
- begin
- Result := goAlwaysShowEditor in Grid.Options;
- end;
-
- function Ctrl: Boolean;
- begin
- Result := ssCtrl in Shift;
- end;
-
- function Selection: TSelection;
- begin
- SendMessage(Handle, EM_GETSEL, Longint(@Result.StartPos), Longint(@Result.EndPos));
- end;
-
- function RightSide: Boolean;
- begin
- with Selection do
- Result := ((StartPos = 0) or (EndPos = StartPos)) and
- (EndPos = GetTextLen);
- end;
-
- function LeftSide: Boolean;
- begin
- with Selection do
- Result := (StartPos = 0) and ((EndPos = 0) or (EndPos = GetTextLen));
- end;
-
- begin
- if (EditStyle in [esButton, esMemo, esGraphic, esBoolean]) and (Key = VK_RETURN) and (Shift = [ssCtrl]) then
- begin
- //<> ctrl-return triggers a button click
- case FEditStyle of
- esButton: Grid.EditButtonClick; //<> custom
- esMemo: Grid.MemoButtonClick; //<> popup memo editor
- esGraphic: Grid.GraphicButtonClick; //<> popup graphic editor
- esBoolean: Grid.BooleanButtonClick; //<> handle boolean
- else
- end;
- end
- else
- case Key of
- VK_UP, VK_DOWN, VK_PRIOR, VK_NEXT, VK_ESCAPE: SendToParent;
- VK_INSERT:
- if Shift = [] then SendToParent
- else if (Shift = [ssShift]) and not Grid.CanEditModify then Key := 0;
- VK_LEFT: if ForwardMovement and (Ctrl or LeftSide) then SendToParent;
- VK_RIGHT: if ForwardMovement and (Ctrl or RightSide) then SendToParent;
- VK_HOME: if ForwardMovement and (Ctrl or LeftSide) then SendToParent;
- VK_END: if ForwardMovement and (Ctrl or RightSide) then SendToParent;
- VK_F2:
- begin
- ParentEvent;
- if Key = VK_F2 then
- begin
- Deselect;
- Exit;
- end;
- end;
- VK_TAB: if not (ssAlt in Shift) then SendToParent;
- end;
- if (Key = VK_DELETE) and not Grid.CanEditModify then Key := 0;
- if Key <> 0 then
- begin
- ParentEvent;
- inherited KeyDown(Key, Shift);
- end;
- end;
-
- procedure TQDBGridInplaceEdit.KeyPress(var Key: Char);
- var
- I: integer;
- Selection: TSelection;
- begin
- Grid.KeyPress(Key);
- if (Key in [#32..#255]) and not Grid.CanEditAcceptKey(Key) then
- begin
- Key := #0;
- MessageBeep(0);
- end;
- case Key of
- #9: Key := #0;
- #27:
- begin
- //<> put back the original value
- Text := Grid.FOriginalText;
- Grid.Cells[Grid.Col, Grid.Row] := Grid.FOriginalText;
- Key := #0;
- end;
- #13:
- begin
- SendMessage(Handle, EM_GETSEL, Longint(@Selection.StartPos), Longint(@Selection.EndPos));
- if (Selection.StartPos = 0) and (Selection.EndPos = GetTextLen) then
- Deselect else
- SelectAll;
- Key := #0;
- end;
- ^H, ^V, ^X, #32..#255:
- if not Grid.CanEditModify then Key := #0;
- end;
- if (Key <> #0) then
- begin
- //<> handle keys to picklist
- if (EditStyle = esPickList) and readonly then
- begin
- //check if picklist was visible...(items are assigned in dropdown proc)
- if not FListVisible and Assigned(FPickList) then
- with Grid do FPickList.items := Columns[Col].Picklist;
-
- for I := 0 to FPicklist.items.count - 1 do if uppercase(copy(FPickList.items[i], 1, 1)) = uppercase(Key) then
- begin
- Text := FPickList.items[i];
- with Grid do SetEditText(col, row, Text);
- modified := true;
- Key := #0;
- break;
- end;
- end;
- inherited KeyPress(Key);
- end;
- end;
-
- procedure TQDBGridInplaceEdit.KeyUp(var Key: Word; Shift: TShiftState);
- begin
- Grid.KeyUp(Key, Shift);
- end;
-
- procedure TQDBGridInplaceEdit.WndProc(var Message: TMessage);
- begin
- case Message.Msg of
- WM_SETFOCUS:
- begin
- if GetParentForm(Self).SetFocusedControl(Grid) then Dispatch(Message);
- Exit;
- end;
- WM_LBUTTONDOWN:
- begin
- if GetMessageTime - FClickTime < GetDoubleClickTime then
- Message.Msg := WM_LBUTTONDBLCLK;
- FClickTime := 0;
- end;
- wm_KeyDown, wm_SysKeyDown, wm_Char:
- if EditStyle in [esPickList] then
- with TWMKey(Message) do
- begin
- DoDropDownKeys(CharCode, KeyDataToShiftState(KeyData));
- if (CharCode <> 0) and FListVisible then
- begin
- with TMessage(Message) do
- SendMessage(FPickList.Handle, Msg, WParam, LParam);
- Exit;
- end;
- end
- end;
- inherited;
- end;
-
- procedure TQDBGridInplaceEdit.Deselect;
- begin
- SendMessage(Handle, EM_SETSEL, $7FFFFFFF, Longint($FFFFFFFF));
- end;
-
- procedure TQDBGridInplaceEdit.Invalidate;
- var
- Cur: TRect;
- begin
- ValidateRect(Handle, nil);
- InvalidateRect(Handle, nil, True);
- Windows.GetClientRect(Handle, Cur);
- MapWindowPoints(Handle, Grid.Handle, Cur, 2);
- ValidateRect(Grid.Handle, @Cur);
- InvalidateRect(Grid.Handle, @Cur, False);
- end;
-
- procedure TQDBGridInplaceEdit.Hide;
- begin
- if HandleAllocated and IsWindowVisible(Handle) then
- begin
- Invalidate;
- SetWindowPos(Handle, 0, 0, 0, 0, 0, SWP_HIDEWINDOW or SWP_NOZORDER or SWP_NOREDRAW);
- if Focused then Windows.SetFocus(Grid.Handle);
- end;
- end;
-
- function TQDBGridInplaceEdit.PosEqual(const Rect: TRect): Boolean;
- var
- Cur: TRect;
- begin
- GetWindowRect(Handle, Cur);
- MapWindowPoints(HWND_DESKTOP, Grid.Handle, Cur, 2);
- Result := EqualRect(Rect, Cur);
- end;
-
- procedure TQDBGridInplaceEdit.InternalMove(const Loc: TRect; Redraw: Boolean);
- begin
- if IsRectEmpty(Loc) then Hide
- else
- begin
- CreateHandle;
- Redraw := Redraw or not IsWindowVisible(Handle);
- Invalidate;
- with Loc do
- SetWindowPos(Handle, HWND_TOP, Left, Top, Right - Left, Bottom - Top,
- SWP_SHOWWINDOW or SWP_NOREDRAW);
- BoundsChanged;
- if Redraw then Invalidate;
- if Grid.Focused then
- Windows.SetFocus(Handle);
- end;
- end;
-
- procedure TQDBGridInplaceEdit.BoundsChanged;
- var
- R: TRect;
- begin
- SetRect(R, 2, 2, Width - 2, Height);
- if FEditStyle <> esSimple then Dec(R.Right, FButtonWidth);
- SendMessage(Handle, EM_SETRECTNP, 0, LongInt(@R));
- SendMessage(Handle, EM_SCROLLCARET, 0, 0);
- end;
-
- procedure TQDBGridInplaceEdit.UpdateLoc(const Loc: TRect);
- begin
- InternalMove(Loc, False);
- end;
-
- function TQDBGridInplaceEdit.Visible: Boolean;
- begin
- Result := IsWindowVisible(Handle);
- end;
-
- procedure TQDBGridInplaceEdit.Move(const Loc: TRect);
- var
- r: TRect;
- begin
- r := Loc;
- //<> adjust vertically because cell is centered and editor isn't
- OffsetRect(r, 0, (abs(Grid.Font.Height) - abs(Grid.Columns[Grid.Col].Font.Height)) div 2);
- InternalMove(r, True);
- end;
-
- procedure TQDBGridInplaceEdit.SetFocus;
- begin
- if IsWindowVisible(Handle) then
- Windows.SetFocus(Handle);
- end;
-
- procedure TQDBGridInplaceEdit.UpdateContents;
- var
- Column: TColumn;
- NewStyle: TEditStyle;
- begin
- with Grid do
- Column := Columns[Col];
- //<> set the edit style based on the button style
- NewStyle := esSimple;
- case Column.ButtonStyle of
- cbsButton: NewStyle := esButton;
- cbsAuto:
- begin
- case Column.FieldType of
- ftstrings, ftrichstrings: NewStyle := esMemo;
- ftgraphic: NewStyle := esGraphic;
- ftboolean: NewStyle := esBoolean;
- else
- end;
- { Show the dropdown button only if the field is editable }
- { Note that a picklist can override the above styles }
- if Assigned(Column.Picklist) and (Column.PickList.Count > 0) and not Column.Readonly then
- begin
- NewStyle := esPickList;
- end;
- end;
- end;
- EditStyle := NewStyle;
- Text := '';
- EditMask := Grid.GetEditMask(Grid.Col, Grid.Row);
- Text := Grid.GetEditText(Grid.Col, Grid.Row);
- MaxLength := Grid.GetEditLimit;
- Grid.FOriginalText := EditText;
- end;
-
- procedure TQDBGridInplaceEdit.CloseUp(Accept: Boolean);
- var
- ListValue: string;
- begin
- if FListVisible then
- begin
- if GetCapture <> 0 then SendMessage(GetCapture, WM_CANCELMODE, 0, 0);
- if FPickList.ItemIndex <> -1 then
- ListValue := FPickList.Items[FPicklist.ItemIndex];
- SetWindowPos(FPickList.Handle, 0, 0, 0, 0, 0, SWP_NOZORDER or
- SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or SWP_HIDEWINDOW);
- FListVisible := False;
- Invalidate;
- if Accept then
- if (ListValue <> '') and (EditCanModify or (not EditCanModify and not Self.Readonly)) then
- with Grid do
- Cells[Col, Row] := ListValue;
- end;
- end;
-
- procedure TQDBGridInplaceEdit.DoDropDownKeys(var Key: Word; Shift: TShiftState);
- begin
- case Key of
- VK_UP, VK_DOWN:
- if ssAlt in Shift then
- begin
- if FListVisible then CloseUp(True) else DropDown;
- Key := 0;
- end;
- VK_RETURN, VK_ESCAPE:
- if FListVisible and not (ssAlt in Shift) then
- begin
- CloseUp(Key = VK_RETURN);
- Key := 0;
- end;
- end;
- end;
-
- procedure TQDBGridInplaceEdit.DropDown;
- var
- P: TPoint;
- Y: Integer;
- Column: TColumn;
- begin
- //<> fill up the picklist
- if not FListVisible and Assigned(FPickList) then
- begin
- FPickList.Width := Width;
- with Grid do
- Column := Columns[Col];
- FPickList.Color := Color;
- FPickList.Font := Font;
- FPickList.Items := Column.Picklist;
- if FPickList.Items.Count >= Column.DropDownRows then
- FPickList.Height := Column.DropDownRows * FPickList.ItemHeight + 4
- else
- FPickList.Height := FPickList.Items.Count * FPickList.ItemHeight + 4;
- with Grid do
- begin
- if Cells[Col, Row] = '' then
- FPickList.ItemIndex := -1
- else
- FPickList.ItemIndex := FPickList.Items.IndexOf(Cells[Col, Row]);
- end;
- P := Parent.ClientToScreen(Point(Left, Top));
- Y := P.Y + Height;
- if Y + FPickList.Height > Screen.Height then Y := P.Y - FPickList.Height;
- SetWindowPos(FPickList.Handle, HWND_TOP, P.X, Y, 0, 0,
- SWP_NOSIZE or SWP_NOACTIVATE or SWP_SHOWWINDOW);
- FListVisible := True;
- Invalidate;
- Windows.SetFocus(Handle);
- end;
- end;
-
- type
- TWinControlCracker = class(TWinControl) end;
-
- procedure TQDBGridInplaceEdit.ListMouseUp(Sender: TObject; Button: TMouseButton;
- Shift: TShiftState; X, Y: Integer);
- begin
- if Button = mbLeft then
- CloseUp(PtInRect(FPickList.ClientRect, Point(X, Y)));
- end;
-
- procedure TQDBGridInplaceEdit.MouseDown(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer);
- begin
- if (Button = mbLeft) and (FEditStyle <> esSimple) and
- PtInRect(Rect(Width - FButtonWidth, 0, Width, Height), Point(X, Y)) then
- begin
- if FListVisible then
- CloseUp(False)
- else
- begin
- MouseCapture := True;
- FTracking := True;
- TrackButton(X, Y);
- if Assigned(FPickList) then
- DropDown;
- end;
- end;
- inherited MouseDown(Button, Shift, X, Y);
- end;
-
- procedure TQDBGridInplaceEdit.MouseMove(Shift: TShiftState; X, Y: Integer);
- var
- ListPos: TPoint;
- MousePos: TSmallPoint;
- begin
- if FTracking then
- begin
- TrackButton(X, Y);
- if FListVisible then
- begin
- ListPos := FPickList.ScreenToClient(ClientToScreen(Point(X, Y)));
- if PtInRect(FPickList.ClientRect, ListPos) then
- begin
- StopTracking;
- MousePos := PointToSmallPoint(ListPos);
- SendMessage(FPickList.Handle, WM_LBUTTONDOWN, 0, Integer(MousePos));
- Exit;
- end;
- end;
- end;
- inherited MouseMove(Shift, X, Y);
- end;
-
- procedure TQDBGridInplaceEdit.MouseUp(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer);
- var
- WasPressed: Boolean;
- begin
- WasPressed := FPressed;
- StopTracking;
- if (Button = mbLeft) and WasPressed then
- begin
- //<> trigger the appropriate button click
- case FEditStyle of
- esButton: Grid.EditButtonClick; //<> custom
- esMemo: Grid.MemoButtonClick; //<> popup memo editor
- esGraphic: Grid.GraphicButtonClick; //<> popup graphic editor
- esBoolean: Grid.BooleanButtonClick; //<> handle boolean
- else
- end;
- end;
- inherited MouseUp(Button, Shift, X, Y);
- end;
-
- procedure TQDBGridInplaceEdit.PaintWindow(DC: HDC);
- var
- R: TRect;
- Flags: Integer;
- begin
- if FEditStyle <> esSimple then
- begin
- SetRect(R, Width - FButtonWidth, 0, Width, Height);
- Flags := 0;
- case FEditStyle of
- esPickList:
- begin
- if FPickList = nil then
- Flags := DFCS_INACTIVE
- else if FPressed then
- Flags := DFCS_FLAT or DFCS_PUSHED;
- //<> draw as if drop-down combo
- DrawFrameControl(DC, R, DFC_SCROLL, Flags or DFCS_SCROLLCOMBOBOX);
- end;
- esBoolean:
- begin
- if FPickList = nil then
- Flags := DFCS_INACTIVE
- else if FPressed then
- Flags := DFCS_FLAT or DFCS_PUSHED;
- //<> draw plain button
- DrawFrameControl(DC, R, DFC_BUTTON, Flags or DFCS_BUTTONPUSH);
- end;
- else { esButton, esMemo, esGraphic}
- begin
- if FPressed then
- Flags := DFCS_FLAT or DFCS_PUSHED;
- //<> draw button with right arrow
- DrawFrameControl(DC, R, DFC_SCROLL, Flags or DFCS_SCROLLRIGHT);
- end;
- end;
- ExcludeClipRect(DC, R.Left, R.Top, R.Right, R.Bottom);
- end;
- inherited PaintWindow(DC);
- end;
-
- procedure TQDBGridInplaceEdit.SetEditStyle(Value: TEditStyle);
- begin
- if Value = FEditStyle then Exit;
- FEditStyle := Value;
- case Value of
- esPickList:
- begin
- if FPickList = nil then
- begin
- FPickList := TPopupListbox.Create(Self);
- FPickList.Visible := False;
- FPickList.Parent := Self;
- FPickList.OnMouseUp := ListMouseUp;
- FPickList.IntegralHeight := True;
- FPickList.ItemHeight := 11;
- end;
- FPickList := FPickList;
- end;
- else { cbsNone, cbsButton, etc., or read only field }
- FPickList := nil;
- end;
- with Grid do
- Self.ReadOnly := Columns[Col].ReadOnly or ((FEditStyle = esPickList) and Columns[Col].LimitTolist); ;
- Repaint;
- end;
-
- procedure TQDBGridInplaceEdit.StopTracking;
- begin
- if FTracking then
- begin
- TrackButton(-1, -1);
- FTracking := False;
- MouseCapture := False;
- end;
- end;
-
- procedure TQDBGridInplaceEdit.TrackButton(X, Y: Integer);
- var
- NewState: Boolean;
- R: TRect;
- begin
- SetRect(R, ClientWidth - FButtonWidth, 0, ClientWidth, ClientHeight);
- NewState := PtInRect(R, Point(X, Y));
- if FPressed <> NewState then
- begin
- FPressed := NewState;
- InvalidateRect(Handle, @R, False);
- end;
- end;
-
- procedure TQDBGridInplaceEdit.CMCancelMode(var Message: TCMCancelMode);
- begin
- if (Message.Sender <> Self) and (Message.Sender <> FPickList) then
- CloseUp(False);
- end;
-
- procedure TQDBGridInplaceEdit.WMCancelMode(var Message: TMessage);
- begin
- StopTracking;
- inherited;
- end;
-
- procedure TQDBGridInplaceEdit.WMKillFocus(var Message: TMessage);
- begin
- inherited;
- CloseUp(False);
- end;
-
- procedure TQDBGridInplaceEdit.WMLButtonDblClk(var Message: TWMLButtonDblClk);
- begin
- with Message do
- if (FEditStyle <> esSimple) and
- PtInRect(Rect(Width - FButtonWidth, 0, Width, Height), Point(XPos, YPos)) then
- Exit;
- inherited;
- end;
-
- procedure TQDBGridInplaceEdit.WMPaint(var Message: TWMPaint);
- begin
- PaintHandler(Message);
- end;
-
- procedure TQDBGridInplaceEdit.WMSetCursor(var Message: TWMSetCursor);
- var
- P: TPoint;
- begin
- GetCursorPos(P);
- if (FEditStyle <> esSimple) and
- PtInRect(Rect(Width - FButtonWidth, 0, Width, Height), ScreenToClient(P)) then
- Windows.SetCursor(LoadCursor(0, idc_Arrow))
- else
- inherited;
- end;
-
- { TQDBGridStrings }
-
- { AIndex < 0 is a column (for column -AIndex - 1)
- AIndex > 0 is a row (for row AIndex - 1)
- AIndex = 0 denotes an empty row or column }
-
- constructor TQDBGridStrings.Create(AGrid: TQDBGrid; AIndex: Longint);
- begin
- inherited Create;
- FGrid := AGrid;
- FIndex := AIndex;
- end;
-
- procedure TQDBGridStrings.Assign(Source: TPersistent);
- var
- I, Max: Integer;
- begin
- if Source is TStrings then
- begin
- BeginUpdate;
- Max := TStrings(Source).Count - 1;
- if Max >= Count then Max := Count - 1;
- try
- for I := 0 to Max do
- begin
- Put(I, TStrings(Source).Strings[I]);
- end;
- finally
- EndUpdate;
- end;
- Exit;
- end;
- inherited Assign(Source);
- end;
-
- procedure TQDBGridStrings.CalcXY(Index: Integer; var X, Y: Integer);
- begin
- if FIndex = 0 then
- begin
- X := -1; Y := -1;
- end else if FIndex > 0 then
- begin
- X := Index;
- Y := FIndex - 1;
- end else
- begin
- X := -FIndex - 1;
- Y := Index;
- end;
- end;
-
- { Changes the meaning of Add to mean copy to the first empty string }
-
- function TQDBGridStrings.Add(const S: string): Integer;
- var
- I: Integer;
- begin
- for I := 0 to Count - 1 do
- if Strings[I] = '' then
- begin
- Strings[I] := S;
- Result := I;
- Exit;
- end;
- Result := -1;
- end;
-
- procedure TQDBGridStrings.Clear;
- var
- SSList: TStringSparseList;
- I: Integer;
-
- function BlankStr(TheIndex: Integer; TheItem: Pointer): Integer; far;
- begin
- Strings[TheIndex] := '';
- Result := 0;
- end;
-
- begin
- if FIndex > 0 then
- begin
- SSList := TStringSparseList(TSparseList(FGrid.FData)[FIndex - 1]);
- if SSList <> nil then SSList.List.ForAll(@BlankStr);
- end
- else if FIndex < 0 then
- for I := Count - 1 downto 0 do Strings[I] := '';
- end;
-
- function TQDBGridStrings.Get(Index: Integer): string;
- var
- X, Y: Integer;
- begin
- CalcXY(Index, X, Y);
- if X < 0 then Result := '' else Result := FGrid.Cells[X, Y];
- end;
-
- function TQDBGridStrings.GetCount: Integer;
- begin
- { Count of a row is the column count, and vice versa }
- if FIndex = 0 then Result := 0
- else if FIndex > 0 then Result := Integer(FGrid.ColCount)
- else Result := Integer(FGrid.RowCount);
- end;
-
- procedure TQDBGridStrings.Put(Index: Integer; const S: string);
- var
- X, Y: Integer;
- begin
- CalcXY(Index, X, Y);
- FGrid.Cells[X, Y] := S;
- end;
-
- procedure TQDBGridStrings.SetUpdateState(Updating: Boolean);
- begin
- FGrid.SetUpdateState(Updating);
- end;
-
- { TQDBGrid }
-
- constructor TQDBGrid.Create(AOwner: TComponent);
- const
- GridStyle = [csCaptureMouse, csOpaque, csDoubleClicks];
- begin
- inherited Create(AOwner);
- if NewStyleControls then
- ControlStyle := GridStyle
- else
- ControlStyle := GridStyle + [csFramed];
- FCanEditModify := True;
- FColCount := 2;
- FRowCount := 5;
- FFixedCols := 1; //<> these never change
- FFixedRows := 1; //<> these never change
- FGridLineWidth := 1;
- FOptions := [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine];
- FFixedColor := clBtnFace;
- FScrollBars := ssBoth;
- FBorderStyle := bsSingle;
- FDefaultColWidth := 64;
- FDefaultRowHeight := abs(Font.Height) + FontHeightMargin;
- FSaveCellExtents := True;
- FEditorMode := False;
- Color := clWindow;
- ParentColor := False;
- TabStop := True;
- Initialize;
- FColumns := CreateColumns;
- FColumns.AddFive; //<> get Columns to match ColCount
- FTitleFont := TFont.Create;
- FTitleFont.OnChange := TitleFontChanged;
- SetBounds(Left, Top, FColCount * FDefaultColWidth, FRowCount * FDefaultRowHeight);
- { make that first row narrow }
- ColWidths[FixedCols - 1] := FixedColWidth;
- end;
-
- destructor TQDBGrid.Destroy;
-
- function FreeItem(TheIndex: Integer; TheItem: Pointer): Integer; far;
- begin
- TObject(TheItem).Free;
- Result := 0;
- end;
-
- begin
- //<> this fiddle stops hideeditor being triggered while destructing
- Options := Options + [goAlwaysShowEditor];
-
- FColumns.Free;
- FColumns := nil;
-
- if FData <> nil then
- begin
- TSparseList(FData).ForAll(@FreeItem);
- TSparseList(FData).Free;
- end;
-
- if FColWidths <> nil then
- begin
- UpdateExtents(FColWidths, 0, DefaultColWidth);
- UpdateExtents(FTabStops, 0, Integer(True));
- end;
- if FRowHeights <> nil then
- UpdateExtents(FRowHeights, 0, DefaultRowHeight);
-
- FInplaceEdit.Free;
-
- FTitleFont.Free;
- FTitleFont := nil;
-
- inherited Destroy;
- end;
-
- procedure TQDBGrid.AdjustSize(Index, Amount: Longint; Rows: Boolean);
- var
- NewCur: TGridCoord;
- MovementX, MovementY: Longint;
- MoveRect: TGridRect;
- ScrollArea: TRect;
- AbsAmount: Longint;
-
- function DoSizeAdjust(var Count: Longint; var Extents: Pointer;
- DefaultExtent: Integer; var Current: Longint): Longint;
- var
- I: Integer;
- NewCount: Longint;
- begin
- NewCount := Count + Amount;
- if NewCount < Index then InvalidOp(STooManyDeleted);
- if (Amount < 0) and Assigned(Extents) then
- begin
- Result := 0;
- for I := Index to Index - Amount - 1 do
- Inc(Result, PIntArray(Extents)^[I]);
- end
- else
- Result := Amount * DefaultExtent;
- if Extents <> nil then
- ModifyExtents(Extents, Index, Amount, DefaultExtent);
- Count := NewCount;
- if Current >= Index then
- if (Amount < 0) and (Current < Index - Amount) then Current := Index
- else Inc(Current, Amount);
- end;
-
- begin
- if Amount = 0 then Exit;
- NewCur := FCurrent;
- MoveRect.Left := FixedCols;
- MoveRect.Right := ColCount - 1;
- MoveRect.Top := FixedRows;
- MoveRect.Bottom := RowCount - 1;
- MovementX := 0;
- MovementY := 0;
- AbsAmount := Amount;
- if AbsAmount < 0 then AbsAmount := -AbsAmount;
- if Rows then
- begin
- MovementY := DoSizeAdjust(FRowCount, FRowHeights, DefaultRowHeight, NewCur.Y);
- MoveRect.Top := Index;
- if Index + AbsAmount <= TopRow then MoveRect.Bottom := TopRow - 1;
- end
- else
- begin
- MovementX := DoSizeAdjust(FColCount, FColWidths, DefaultColWidth, NewCur.X);
- MoveRect.Left := Index;
- if Index + AbsAmount <= LeftCol then MoveRect.Right := LeftCol - 1;
- end;
- GridRectToScreenRect(MoveRect, ScrollArea, True);
- if not IsRectEmpty(ScrollArea) then
- begin
- ScrollWindow(Handle, MovementX, MovementY, @ScrollArea, @ScrollArea);
- UpdateWindow(Handle);
- end;
- if (NewCur.X <> FCurrent.X) or (NewCur.Y <> FCurrent.Y) then
- MoveCurrent(NewCur.X, NewCur.Y, True, True);
- end;
-
- procedure TQDBGrid.SetTitleFont(Value: TFont);
- begin
- FTitleFont.Assign(Value);
- RowHeights[0] := abs(TitleFont.Height) + FontHeightMargin;
- InvalidateGrid;
- end;
-
- procedure TQDBGrid.TitleFontChanged(Sender: TObject);
- begin
- end;
-
- function TQDBGrid.BoxRect(ALeft, ATop, ARight, ABottom: Longint): TRect;
- var
- GridRect: TGridRect;
- begin
- GridRect.Left := ALeft;
- GridRect.Right := ARight;
- GridRect.Top := ATop;
- GridRect.Bottom := ABottom;
- GridRectToScreenRect(GridRect, Result, False);
- end;
-
- procedure TQDBGrid.DoExit;
- begin
- inherited DoExit;
- if not (goAlwaysShowEditor in Options) then HideEditor;
- end;
-
- function TQDBGrid.CellRect(ACol, ARow: Longint): TRect;
- begin
- Result := BoxRect(ACol, ARow, ACol, ARow);
- end;
-
- function TQDBGrid.CanEditAcceptKey(Key: Char): Boolean;
- begin
- Result := True;
- end;
-
- function TQDBGrid.CanGridAcceptKey(Key: Word; Shift: TShiftState): Boolean;
- begin
- Result := True;
- end;
-
- function TQDBGrid.CanEditModify: Boolean;
- begin
- Result := (goEditing in Options) and not QDBItem.ReadOnly;
- if Result and (Columns.count > Col) then
- with Columns[Col] do
- result := not ReadOnly;
- //<> the following fields cannot be edited as text
- if Columns[Col].FieldType in [ftboolean, ftgraphic, ftstrings, ftrichstrings] then Result := false;
- end;
-
- function TQDBGrid.CanEditShow: Boolean;
- begin
- Result := ([goEditing] * Options = [goEditing]) and
- FEditorMode and not (csDesigning in ComponentState) and HandleAllocated and
- ((goAlwaysShowEditor in Options) or (ValidParentForm(Self).ActiveControl = Self));
- end;
-
- procedure TQDBGrid.DefaultHandler(var Msg);
- var
- P: TPopupMenu;
- Cell: TGridCoord;
- begin
- inherited DefaultHandler(Msg);
- if TMessage(Msg).Msg = wm_RButtonUp then
- with TWMRButtonUp(Msg) do
- begin
- Cell := MouseCoord(XPos, YPos);
- if (Cell.X < 0) or (Cell.Y < 0) then Exit;
- P := Columns[Cell.X].PopupMenu;
- if (P <> nil) and P.AutoPopup then
- begin
- SendCancelMode(nil);
- P.PopupComponent := Self;
- with ClientToScreen(SmallPointToPoint(Pos)) do
- P.Popup(X, Y);
- Result := 1;
- end;
- end;
- end;
-
- function TQDBGrid.GetEditLimit: Integer;
- begin
- Result := 0;
- end;
-
- procedure TQDBGrid.HideEditor;
- begin
- FEditorMode := False;
- try
- HideEdit;
- except
- //<> handle OnInvalidValue to change default behavior
- if Assigned(Columns[Col].FOnInvalidValue) then
- Columns[Col].FOnInvalidvalue(self)
- else
- MessageBeep(0);
- end;
- end;
-
- procedure TQDBGrid.ShowEditor;
- begin
- FEditorMode := True;
- UpdateEdit;
- end;
-
- procedure TQDBGrid.ShowEditorChar(Ch: Char);
- begin
- ShowEditor;
- if FInplaceEdit <> nil then
- PostMessage(FInplaceEdit.Handle, WM_CHAR, Word(Ch), 0);
- end;
-
- procedure TQDBGrid.InvalidateEditor;
- begin
- FInplaceCol := -1;
- FInplaceRow := -1;
- UpdateEdit;
- end;
-
- procedure TQDBGrid.ReadColCount(Reader: TReader);
- begin
- with Reader do
- begin
- ReadListBegin;
- ColCount := ReadInteger;
- ReadListEnd;
- end;
- end;
-
- procedure TQDBGrid.ReadColWidths(Reader: TReader);
- var
- I: Integer;
- begin
- with Reader do
- begin
- ReadListBegin;
- for I := 0 to ColCount - 1 do ColWidths[I] := ReadInteger;
- ReadListEnd;
- end;
- end;
-
- procedure TQDBGrid.ReadRowHeights(Reader: TReader);
- var
- I: Integer;
- begin
- with Reader do
- begin
- ReadListBegin;
- for I := 0 to RowCount - 1 do RowHeights[I] := ReadInteger;
- ReadListEnd;
- end;
- end;
-
- procedure TQDBGrid.WriteColCount(Writer: TWriter);
- begin
- with Writer do
- begin
- WriteListBegin;
- WriteInteger(ColCount);
- WriteListEnd;
- end;
- end;
-
- procedure TQDBGrid.WriteColWidths(Writer: TWriter);
- var
- I: Integer;
- begin
- with Writer do
- begin
- WriteListBegin;
- for I := 0 to ColCount - 1 do WriteInteger(ColWidths[I]);
- WriteListEnd;
- end;
- end;
-
- procedure TQDBGrid.WriteRowHeights(Writer: TWriter);
- var
- I: Integer;
- begin
- with Writer do
- begin
- WriteListBegin;
- for I := 0 to RowCount - 1 do WriteInteger(RowHeights[I]);
- WriteListEnd;
- end;
- end;
-
- procedure TQDBGrid.DefineProperties(Filer: TFiler);
-
- function DoColCount: Boolean;
- begin
- if Filer.Ancestor <> nil then
- Result := FColCount <> TQDBGrid(Filer.Ancestor).FColCount
- else
- Result := FColWidths <> nil;
- end;
-
- function DoColWidths: Boolean;
- begin
- if Filer.Ancestor <> nil then
- Result := not CompareExtents(TQDBGrid(Filer.Ancestor).FColWidths, FColWidths)
- else
- Result := FColWidths <> nil;
- end;
-
- function DoRowHeights: Boolean;
- begin
- if Filer.Ancestor <> nil then
- Result := not CompareExtents(TQDBGrid(Filer.Ancestor).FRowHeights, FRowHeights)
- else
- Result := FRowHeights <> nil;
- end;
-
- begin
- inherited DefineProperties(Filer);
- if FSaveCellExtents then
- with Filer do
- begin
- DefineProperty('ColCount', ReadColCount, WriteColCount, DoColCount);
- DefineProperty('ColWidths', ReadColWidths, WriteColWidths, DoColWidths);
- DefineProperty('RowHeights', ReadRowHeights, WriteRowHeights, DoRowHeights);
- end;
- end;
-
- procedure TQDBGrid.MoveColumn(FromIndex, ToIndex: Longint);
- var
- Rect: TGridRect;
- begin
- if FromIndex = ToIndex then Exit;
- if Assigned(FColWidths) then
- begin
- MoveExtent(FColWidths, FromIndex + 1, ToIndex + 1);
- MoveExtent(FTabStops, FromIndex + 1, ToIndex + 1);
- end;
- MoveAdjust(FCurrent.X, FromIndex, ToIndex);
- MoveAdjust(FAnchor.X, FromIndex, ToIndex);
- MoveAdjust(FInplaceCol, FromIndex, ToIndex);
- Rect.Top := 0;
- Rect.Bottom := VisibleRowCount;
- if FromIndex < ToIndex then
- begin
- Rect.Left := FromIndex;
- Rect.Right := ToIndex;
- end
- else
- begin
- Rect.Left := ToIndex;
- Rect.Right := FromIndex;
- end;
- InvalidateRect(Rect);
- ColumnMoved(FromIndex, ToIndex);
- ReLoad;
- if Assigned(FColWidths) then
- ColWidthsChanged;
- UpdateEdit;
- end;
-
- procedure TQDBGrid.MoveRow(FromIndex, ToIndex: Longint);
- begin
- if Assigned(FRowHeights) then
- MoveExtent(FRowHeights, FromIndex + 1, ToIndex + 1);
- MoveAdjust(FCurrent.Y, FromIndex, ToIndex);
- MoveAdjust(FAnchor.Y, FromIndex, ToIndex);
- MoveAdjust(FInplaceRow, FromIndex, ToIndex);
- RowMoved(FromIndex, ToIndex);
- if Assigned(FRowHeights) then
- RowHeightsChanged;
- UpdateEdit;
- end;
-
- function TQDBGrid.MouseCoord(X, Y: Integer): TGridCoord;
- var
- DrawInfo: TGridDrawInfo;
- begin
- CalcDrawInfo(DrawInfo);
- Result := CalcCoordFromPoint(X, Y, DrawInfo);
- if Result.X < 0 then Result.Y := -1
- else if Result.Y < 0 then Result.X := -1;
- end;
-
- procedure TQDBGrid.MoveColRow(ACol, ARow: Longint; MoveAnchor,
- Show: Boolean);
- begin
- MoveCurrent(ACol, ARow, MoveAnchor, Show);
- end;
-
- //<> Selectcell is usually used to decide whether a cell can be selected
- // here we always return true but we also check to see if there is a
- // pending row addition to process
- function TQDBGrid.SelectCell(ACol, ARow: Longint): Boolean;
- var
- k: TKey;
- begin
- Result := True;
- //<> check if we are still in a newly added row and if not store the newly added row }
- if Adding and (ARow <> Row) then
- begin
- SaveRow(Row);
- { if the newly added row was unchanged we delete it }
- FQDBItem.OnKey(self, k);
- if k = '' then
- begin
- FQDBItem.Delete;
- RowCount := RowCount - 1;
- end;
- { then display the visible rows }
- ReLoad;
- Adding := false;
- end;
- if Assigned(FOnSelectCell) then FOnSelectCell(Self, ACol, ARow, Result);
- end;
-
- function TQDBGrid.Sizing(X, Y: Integer): Boolean;
- var
- FixedInfo: TGridDrawInfo;
- State: TGridState;
- Index: Longint;
- Pos, Ofs: Integer;
- begin
- State := FGridState;
- if State = gsNormal then
- begin
- CalcFixedInfo(FixedInfo);
- CalcSizingState(X, Y, State, Index, Pos, Ofs, FixedInfo);
- end;
- Result := State <> gsNormal;
- end;
-
- procedure TQDBGrid.TopLeftChanged;
- begin
- if FEditorMode and (FInplaceEdit <> nil) then FInplaceEdit.UpdateLoc(CellRect(Col, Row));
- if Assigned(FOnTopLeftChanged) then FOnTopLeftChanged(Self);
- end;
-
- procedure FillDWord(var Dest; Count, Value: Integer); register;
- asm
- XCHG EDX, ECX
- PUSH EDI
- MOV EDI, EAX
- MOV EAX, EDX
- REP STOSD
- POP EDI
- end;
-
- { StackAlloc allocates a 'small' block of FMemory from the stack by
- decrementing SP. This provides the allocation speed of a local variable,
- but the runtime size flexibility of heap allocated memory. }
-
- function StackAlloc(Size: Integer): Pointer; register;
- asm
- POP ECX { return address }
- MOV EDX, ESP
- SUB ESP, EAX
- MOV EAX, ESP { function result = low memory address of block }
- PUSH EDX { save original SP, for cleanup }
- MOV EDX, ESP
- SUB EDX, 4
- PUSH EDX { save current SP, for sanity check (sp = [sp]) }
- PUSH ECX { return to caller }
- end;
-
- { StackFree pops the memory allocated by StackAlloc off the stack.
- - Calling StackFree is optional - SP will be restored when the calling routine
- exits, but it's a good idea to free the stack allocated memory ASAP anyway.
- - StackFree must be called in the same stack context as StackAlloc - not in
- a subroutine or finally block.
- - Multiple StackFree calls must occur in reverse order of their corresponding
- StackAlloc calls.
- - Built-in sanity checks guarantee that an improper call to StackFree will not
- corrupt the stack. Worst case is that the stack block is not released until
- the calling routine exits. }
-
- procedure StackFree(P: Pointer); register;
- asm
- POP ECX { return address }
- MOV EDX, DWORD PTR [ESP]
- SUB EAX, 8
- CMP EDX, ESP { sanity check #1 (SP = [SP]) }
- JNE @@1
- CMP EDX, EAX { sanity check #2 (P = this stack block) }
- JNE @@1
- MOV ESP, DWORD PTR [ESP+4] { restore previous SP }
- @@1:
- PUSH ECX { return to caller }
- end;
-
- procedure TQDBGrid.Paint;
- var
- LineColor: TColor;
- DrawInfo: TGridDrawInfo;
- Sel: TGridRect;
- UpdateRect: TRect;
- PointsList: PIntArray;
- StrokeList: PIntArray;
- MaxStroke: Integer;
-
- procedure DrawLines(DoHorz, DoVert: Boolean; Col, Row: Longint;
- const CellBounds: array of Integer; OnColor, OffColor: TColor);
-
- { Cellbounds is 4 integers: StartX, StartY, StopX, StopY
- Horizontal lines: MajorIndex = 0
- Vertical lines: MajorIndex = 1 }
-
- const
- FlatPenStyle = PS_Geometric or PS_Solid or PS_EndCap_Flat or PS_Join_Miter;
-
- procedure DrawAxisLines(const AxisInfo: TGridAxisDrawInfo;
- Cell, MajorIndex: Integer; UseOnColor: Boolean);
- var
- Line: Integer;
- LogBrush: TLOGBRUSH;
- Index: Integer;
- Points: PIntArray;
- StopMajor, StartMinor, StopMinor: Integer;
- begin
- with Canvas, AxisInfo do
- begin
- if EffectiveLineWidth <> 0 then
- begin
- Pen.Width := GridLineWidth;
- if UseOnColor then
- Pen.Color := OnColor
- else
- Pen.Color := OffColor;
- if Pen.Width > 1 then
- begin
- LogBrush.lbStyle := BS_Solid;
- LogBrush.lbColor := Pen.Color;
- LogBrush.lbHatch := 0;
- Pen.Handle := ExtCreatePen(FlatPenStyle, Pen.Width, LogBrush, 0, nil);
- end;
- Points := PointsList;
- Line := CellBounds[MajorIndex] + EffectiveLineWidth shr 1 +
- GetExtent(Cell);
- StartMinor := CellBounds[MajorIndex xor 1];
- StopMinor := CellBounds[2 + (MajorIndex xor 1)];
- StopMajor := CellBounds[2 + MajorIndex] + EffectiveLineWidth;
- Index := 0;
- repeat
- Points^[Index + MajorIndex] := Line; { MoveTo }
- Points^[Index + (MajorIndex xor 1)] := StartMinor;
- Inc(Index, 2);
- Points^[Index + MajorIndex] := Line; { LineTo }
- Points^[Index + (MajorIndex xor 1)] := StopMinor;
- Inc(Index, 2);
- Inc(Cell);
- Inc(Line, GetExtent(Cell) + EffectiveLineWidth);
- until Line > StopMajor;
- { 2 integers per point, 2 points per line -> Index div 4 }
- PolyPolyLine(Canvas.Handle, Points^, StrokeList^, Index shr 2);
- end;
- end;
- end;
-
- begin
- if (CellBounds[0] = CellBounds[2]) or (CellBounds[1] = CellBounds[3]) then Exit;
- if not DoHorz then
- begin
- DrawAxisLines(DrawInfo.Vert, Row, 1, DoHorz);
- DrawAxisLines(DrawInfo.Horz, Col, 0, DoVert);
- end
- else
- begin
- DrawAxisLines(DrawInfo.Horz, Col, 0, DoVert);
- DrawAxisLines(DrawInfo.Vert, Row, 1, DoHorz);
- end;
- end;
-
- procedure DrawCells(ACol, ARow: Longint; StartX, StartY, StopX, StopY: Integer;
- Color: TColor; IncludeDrawState: TGridDrawState);
- var
- CurCol, CurRow: Longint;
- Where: TRect;
- DrawState: TGridDrawState;
- Focused: Boolean;
- begin
- CurRow := ARow;
- Where.Top := StartY;
- while (Where.Top < StopY) and (CurRow < RowCount) do
- begin
- CurCol := ACol;
- Where.Left := StartX;
- Where.Bottom := Where.Top + RowHeights[CurRow];
- while (Where.Left < StopX) and (CurCol < ColCount) do
- begin
- Where.Right := Where.Left + ColWidths[CurCol];
- if RectVisible(Canvas.Handle, Where) then
- begin
- DrawState := IncludeDrawState;
- Focused := ValidParentForm(Self).ActiveControl = Self;
- if Focused and (CurRow = Row) and (CurCol = Col) then
- Include(DrawState, gdFocused);
- if PointInGridRect(CurCol, CurRow, Sel) then
- Include(DrawState, gdSelected);
- if not (gdFocused in DrawState) or not (goEditing in Options) or not FEditorMode or (csDesigning in ComponentState) then
- begin
- DrawCell(CurCol, CurRow, Where, DrawState);
- if (gdFixed in DrawState) and Ctl3D then
- begin
- DrawEdge(Canvas.Handle, Where, BDR_RAISEDINNER, BF_BOTTOMRIGHT);
- DrawEdge(Canvas.Handle, Where, BDR_RAISEDINNER, BF_TOPLEFT);
- end;
- if not (csDesigning in ComponentState) and
- (gdFocused in DrawState) and
- ([goEditing, goAlwaysShowEditor] * Options <>
- [goEditing, goAlwaysShowEditor]) then
- DrawFocusRect(Canvas.Handle, Where);
- end;
- end;
- Where.Left := Where.Right + DrawInfo.Horz.EffectiveLineWidth;
- Inc(CurCol);
- end;
- Where.Top := Where.Bottom + DrawInfo.Vert.EffectiveLineWidth;
- Inc(CurRow);
- end;
- end;
-
- begin
- UpdateRect := Canvas.ClipRect;
- CalcDrawInfo(DrawInfo);
- with DrawInfo do
- begin
- if (Horz.EffectiveLineWidth > 0) or (Vert.EffectiveLineWidth > 0) then
- begin
- { Draw the grid line in the four areas (fixed, fixed), (variable, fixed),
- (fixed, variable) and (variable, variable) }
- LineColor := clSilver;
- MaxStroke := IMax(Horz.LastFullVisibleCell - LeftCol + FixedCols,
- Vert.LastFullVisibleCell - TopRow + FixedRows) + 3;
- PointsList := StackAlloc(MaxStroke * sizeof(TPoint) * 2);
- StrokeList := StackAlloc(MaxStroke * sizeof(Integer));
- FillDWord(StrokeList^, MaxStroke, 2);
-
- if ColorToRGB(Color) = clSilver then LineColor := clGray;
- DrawLines(goFixedHorzLine in Options, goFixedVertLine in Options,
- 0, 0, [0, 0, Horz.FixedBoundary, Vert.FixedBoundary], clBlack, FixedColor);
- DrawLines(goFixedHorzLine in Options, goFixedVertLine in Options,
- LeftCol, 0, [Horz.FixedBoundary, 0, Horz.GridBoundary,
- Vert.FixedBoundary], clBlack, FixedColor);
- DrawLines(goFixedHorzLine in Options, goFixedVertLine in Options,
- 0, TopRow, [0, Vert.FixedBoundary, Horz.FixedBoundary,
- Vert.GridBoundary], clBlack, FixedColor);
- DrawLines(goHorzLine in Options, goVertLine in Options, LeftCol,
- TopRow, [Horz.FixedBoundary, Vert.FixedBoundary, Horz.GridBoundary,
- Vert.GridBoundary], LineColor, Color);
-
- StackFree(StrokeList);
- StackFree(PointsList);
- end;
-
- Sel := Selection;
- DrawCells(0, 0, 0, 0, Horz.FixedBoundary, Vert.FixedBoundary, FixedColor,
- [gdFixed]);
- DrawCells(LeftCol, 0, Horz.FixedBoundary - FColOffset, 0, Horz.GridBoundary, //!! clip
- Vert.FixedBoundary, FixedColor, [gdFixed]);
- DrawCells(0, TopRow, 0, Vert.FixedBoundary, Horz.FixedBoundary,
- Vert.GridBoundary, FixedColor, [gdFixed]);
- DrawCells(LeftCol, TopRow, Horz.FixedBoundary - FColOffset, //!! clip
- Vert.FixedBoundary, Horz.GridBoundary, Vert.GridBoundary, Color, []);
-
- { Fill in area not occupied by cells }
- if Horz.GridBoundary < Horz.GridExtent then
- begin
- Canvas.Brush.Color := Color;
- Canvas.FillRect(Rect(Horz.GridBoundary, 0, Horz.GridExtent, Vert.GridBoundary));
- end;
- if Vert.GridBoundary < Vert.GridExtent then
- begin
- Canvas.Brush.Color := Color;
- Canvas.FillRect(Rect(0, Vert.GridBoundary, Horz.GridExtent, Vert.GridExtent));
- end;
- end;
- end;
-
- function TQDBGrid.CalcCoordFromPoint(X, Y: Integer;
- const DrawInfo: TGridDrawInfo): TGridCoord;
-
- function DoCalc(const AxisInfo: TGridAxisDrawInfo; N: Integer): Integer;
- var
- I, Start, Stop: Longint;
- Line: Integer;
- begin
- with AxisInfo do
- begin
- if N < FixedBoundary then
- begin
- Start := 0;
- Stop := FixedCellCount - 1;
- Line := 0;
- end
- else
- begin
- Start := FirstGridCell;
- Stop := GridCellCount - 1;
- Line := FixedBoundary;
- end;
- Result := -1;
- for I := Start to Stop do
- begin
- Inc(Line, GetExtent(I) + EffectiveLineWidth);
- if N < Line then
- begin
- Result := I;
- Exit;
- end;
- end;
- end;
- end;
-
- begin
- Result.X := DoCalc(DrawInfo.Horz, X);
- Result.Y := DoCalc(DrawInfo.Vert, Y);
- end;
-
- procedure TQDBGrid.CalcDrawInfo(var DrawInfo: TGridDrawInfo);
- begin
- CalcDrawInfoXY(DrawInfo, ClientWidth, ClientHeight);
- end;
-
- procedure TQDBGrid.CalcDrawInfoXY(var DrawInfo: TGridDrawInfo;
- UseWidth, UseHeight: Integer);
-
- procedure CalcAxis(var AxisInfo: TGridAxisDrawInfo; UseExtent: Integer);
- var
- I: Integer;
- begin
- with AxisInfo do
- begin
- GridExtent := UseExtent;
- GridBoundary := FixedBoundary;
- FullVisBoundary := FixedBoundary;
- LastFullVisibleCell := FirstGridCell;
- for I := FirstGridCell to GridCellCount - 1 do
- begin
- Inc(GridBoundary, GetExtent(I) + EffectiveLineWidth);
- if GridBoundary > GridExtent + EffectiveLineWidth then
- begin
- GridBoundary := GridExtent;
- Break;
- end;
- LastFullVisibleCell := I;
- FullVisBoundary := GridBoundary;
- end;
- end;
- end;
-
- begin
- CalcFixedInfo(DrawInfo);
- CalcAxis(DrawInfo.Horz, UseWidth);
- CalcAxis(DrawInfo.Vert, UseHeight);
- end;
-
- procedure TQDBGrid.CalcFixedInfo(var DrawInfo: TGridDrawInfo);
-
- procedure CalcFixedAxis(var Axis: TGridAxisDrawInfo; LineOptions: TGridOptions;
- FixedCount, FirstCell, CellCount: Integer; GetExtentFunc: TGetExtentsFunc);
- var
- I: Integer;
- begin
- with Axis do
- begin
- if LineOptions * Options = [] then
- EffectiveLineWidth := 0
- else
- EffectiveLineWidth := GridLineWidth;
-
- FixedBoundary := 0;
- for I := 0 to FixedCount - 1 do
- Inc(FixedBoundary, GetExtentFunc(I) + EffectiveLineWidth);
-
- FixedCellCount := FixedCount;
- FirstGridCell := FirstCell;
- GridCellCount := CellCount;
- GetExtent := GetExtentFunc;
- end;
- end;
-
- begin
- CalcFixedAxis(DrawInfo.Horz, [goFixedVertLine, goVertLine], FixedCols,
- LeftCol, ColCount, GetColWidths);
- CalcFixedAxis(DrawInfo.Vert, [goFixedHorzLine, goHorzLine], FixedRows,
- TopRow, RowCount, GetRowHeights);
- end;
-
- { Calculates the TopLeft that will put the given Coord in view }
-
- function TQDBGrid.CalcMaxTopLeft(const Coord: TGridCoord;
- const DrawInfo: TGridDrawInfo): TGridCoord;
-
- function CalcMaxCell(const Axis: TGridAxisDrawInfo; Start: Integer): Integer;
- var
- Line: Integer;
- I: Longint;
- begin
- Result := Start;
- with Axis do
- begin
- Line := GridExtent + EffectiveLineWidth;
- for I := Start downto FixedCellCount do
- begin
- Dec(Line, GetExtent(I));
- Dec(Line, EffectiveLineWidth);
- if Line < FixedBoundary then Break;
- Result := I;
- end;
- end;
- end;
-
- begin
- Result.X := CalcMaxCell(DrawInfo.Horz, Coord.X);
- Result.Y := CalcMaxCell(DrawInfo.Vert, Coord.Y);
- end;
-
- procedure TQDBGrid.CalcSizingState(X, Y: Integer; var State: TGridState;
- var Index: Longint; var SizingPos, SizingOfs: Integer;
- var FixedInfo: TGridDrawInfo);
-
- procedure CalcAxisState(const AxisInfo: TGridAxisDrawInfo; Pos: Integer;
- NewState: TGridState);
- var
- I, Line, Back, Range: Integer;
- begin
- with AxisInfo do
- begin
- Line := FixedBoundary;
- Range := EffectiveLineWidth;
- Back := 0;
- if Range < 7 then
- begin
- Range := 7;
- Back := (Range - EffectiveLineWidth) shr 1;
- end;
- for I := FirstGridCell to GridCellCount - 1 do
- begin
- Inc(Line, GetExtent(I));
- if Line > GridExtent then Break;
- if (Pos >= Line - Back) and (Pos <= Line - Back + Range) then
- begin
- State := NewState;
- SizingPos := Line;
- SizingOfs := Line - Pos;
- Index := I;
- Exit;
- end;
- Inc(Line, EffectiveLineWidth);
- end;
- if (Pos >= GridExtent - Back) and (Pos <= GridExtent) then
- begin
- State := NewState;
- SizingPos := GridExtent;
- SizingOfs := GridExtent - Pos;
- Index := I;
- end;
- end;
- end;
-
- var
- EffectiveOptions: TGridOptions;
- begin
- State := gsNormal;
- Index := -1;
- EffectiveOptions := Options;
- if csDesigning in ComponentState then
- begin
- Include(EffectiveOptions, goColSizing);
- Include(EffectiveOptions, goRowSizing);
- end;
- if [goColSizing, goRowSizing] * EffectiveOptions <> [] then
- with FixedInfo do
- begin
- Vert.GridExtent := ClientHeight;
- Horz.GridExtent := ClientWidth;
- if (X > Horz.FixedBoundary) and (goColSizing in EffectiveOptions) then
- begin
- if Y >= Vert.FixedBoundary then Exit;
- CalcAxisState(Horz, X, gsColSizing);
- end
- else if (Y > Vert.FixedBoundary) and (goRowSizing in EffectiveOptions) then
- begin
- if X >= Horz.FixedBoundary then Exit;
- CalcAxisState(Vert, Y, gsRowSizing);
- end;
- end;
- end;
-
- procedure TQDBGrid.ChangeSize(NewColCount, NewRowCount: Longint);
- var
- OldColCount, OldRowCount: Longint;
-
- procedure DoChange;
- var
- Coord: TGridCoord;
- begin
- if FColWidths <> nil then
- begin
- UpdateExtents(FColWidths, ColCount, DefaultColWidth);
- UpdateExtents(FTabStops, ColCount, Integer(True));
- end;
- if FRowHeights <> nil then
- UpdateExtents(FRowHeights, RowCount, DefaultRowHeight);
- Coord := FCurrent;
- if Row >= RowCount then Coord.Y := RowCount - 1;
- if Col >= ColCount then Coord.X := ColCount - 1;
- if (FCurrent.X <> Coord.X) or (FCurrent.Y <> Coord.Y) then
- MoveCurrent(Coord.X, Coord.Y, True, True);
- if (FAnchor.X <> Coord.X) or (FAnchor.Y <> Coord.Y) then
- MoveAnchor(Coord);
- InvalidateGrid;
- UpdateScrollRange;
- end;
-
- begin
- OldColCount := FColCount;
- OldRowCount := FRowCount;
- FColCount := NewColCount;
- FRowCount := NewRowCount;
- try
- DoChange;
- except
- { Could not change size so try to clean up by setting the size back }
- FColCount := OldColCount;
- FRowCount := OldRowCount;
- DoChange;
- raise;
- end;
- InvalidateGrid;
- end;
-
- { Will move TopLeft so that Coord is in view }
-
- procedure TQDBGrid.ClampInView(const Coord: TGridCoord);
- var
- DrawInfo: TGridDrawInfo;
- MaxTopLeft: TGridCoord;
- OldTopLeft: TGridCoord;
- begin
- if not HandleAllocated then Exit;
- CalcDrawInfo(DrawInfo);
- with DrawInfo, Coord do
- begin
- if (X > Horz.LastFullVisibleCell) or
- (Y > Vert.LastFullVisibleCell) or (X < LeftCol) or (Y < TopRow) then
- begin
- OldTopLeft := FTopLeft;
- MaxTopLeft := CalcMaxTopLeft(Coord, DrawInfo);
- inherited Update;
- if X < LeftCol then FTopLeft.X := X
- else if X > Horz.LastFullVisibleCell then FTopLeft.X := MaxTopLeft.X;
- if Y < TopRow then FTopLeft.Y := Y
- else if Y > Vert.LastFullVisibleCell then FTopLeft.Y := MaxTopLeft.Y;
- TopLeftMoved(OldTopLeft);
- end;
- end;
- end;
-
- procedure TQDBGrid.DrawSizingLine(const DrawInfo: TGridDrawInfo);
- var
- OldPen: TPen;
- begin
- OldPen := TPen.Create;
- try
- with Canvas, DrawInfo do
- begin
- OldPen.Assign(Pen);
- Pen.Style := psDot;
- Pen.Mode := pmXor;
- Pen.Width := 1;
- try
- if FGridState = gsRowSizing then
- begin
- MoveTo(0, FSizingPos);
- LineTo(Horz.GridBoundary, FSizingPos);
- end
- else
- begin
- MoveTo(FSizingPos, 0);
- LineTo(FSizingPos, Vert.GridBoundary);
- end;
- finally
- Pen := OldPen;
- end;
- end;
- finally
- OldPen.Free;
- end;
- end;
-
- procedure TQDBGrid.DrawMove;
- var
- OldPen: TPen;
- Pos: Integer;
- R: TRect;
- begin
- OldPen := TPen.Create;
- try
- with Canvas do
- begin
- OldPen.Assign(Pen);
- try
- Pen.Style := psDot;
- Pen.Mode := pmXor;
- Pen.Width := 5;
- R := CellRect(FMovePos, 0);
- if FMovePos > FMoveIndex then
- Pos := R.Right else
- Pos := R.Left;
- MoveTo(Pos, 0);
- LineTo(Pos, ClientHeight);
- finally
- Canvas.Pen := OldPen;
- end;
- end;
- finally
- OldPen.Free;
- end;
- end;
-
- procedure TQDBGrid.FocusCell(ACol, ARow: Longint; MoveAnchor: Boolean);
- begin
- MoveCurrent(ACol, ARow, MoveAnchor, True);
- UpdateEdit;
- Click;
- end;
-
- procedure TQDBGrid.GridRectToScreenRect(GridRect: TGridRect;
- var ScreenRect: TRect; IncludeLine: Boolean);
-
- function LinePos(const AxisInfo: TGridAxisDrawInfo; Line: Integer): Integer;
- var
- Start, I: Longint;
- begin
- with AxisInfo do
- begin
- Result := 0;
- if Line < FixedCellCount then
- Start := 0
- else
- begin
- if Line >= FirstGridCell then
- Result := FixedBoundary;
- Start := FirstGridCell;
- end;
- for I := Start to Line - 1 do
- begin
- Inc(Result, GetExtent(I) + EffectiveLineWidth);
- if Result > GridExtent then
- begin
- Result := 0;
- Exit;
- end;
- end;
- end;
- end;
-
- function CalcAxis(const AxisInfo: TGridAxisDrawInfo;
- GridRectMin, GridRectMax: Integer;
- var ScreenRectMin, ScreenRectMax: Integer): Boolean;
- begin
- Result := False;
- with AxisInfo do
- begin
- if (GridRectMin >= FixedCellCount) and (GridRectMin < FirstGridCell) then
- if GridRectMax < FirstGridCell then
- begin
- FillChar(ScreenRect, SizeOf(ScreenRect), 0); { erase partial results }
- Exit;
- end
- else
- GridRectMin := FirstGridCell;
- if GridRectMax > LastFullVisibleCell then
- begin
- GridRectMax := LastFullVisibleCell;
- if GridRectMax < GridCellCount - 1 then Inc(GridRectMax);
- if LinePos(AxisInfo, GridRectMax) = 0 then
- Dec(GridRectMax);
- end;
-
- ScreenRectMin := LinePos(AxisInfo, GridRectMin);
- ScreenRectMax := LinePos(AxisInfo, GridRectMax);
- if ScreenRectMax = 0 then
- ScreenRectMax := ScreenRectMin + GetExtent(GridRectMin)
- else
- Inc(ScreenRectMax, GetExtent(GridRectMax));
- if ScreenRectMax > GridExtent then
- ScreenRectMax := GridExtent;
- if IncludeLine then Inc(ScreenRectMax, EffectiveLineWidth);
- end;
- Result := True;
- end;
-
- var
- DrawInfo: TGridDrawInfo;
- begin
- FillChar(ScreenRect, SizeOf(ScreenRect), 0);
- if (GridRect.Left > GridRect.Right) or (GridRect.Top > GridRect.Bottom) then
- Exit;
- CalcDrawInfo(DrawInfo);
- with DrawInfo do
- begin
- if GridRect.Left > Horz.LastFullVisibleCell + 1 then Exit;
- if GridRect.Top > Vert.LastFullVisibleCell + 1 then Exit;
-
- if CalcAxis(Horz, GridRect.Left, GridRect.Right, ScreenRect.Left,
- ScreenRect.Right) then
- begin
- CalcAxis(Vert, GridRect.Top, GridRect.Bottom, ScreenRect.Top,
- ScreenRect.Bottom);
- end;
- end;
- end;
-
- procedure TQDBGrid.InvalidateCell(ACol, ARow: Longint);
- var
- Rect: TGridRect;
- begin
- Rect.Top := ARow;
- Rect.Left := ACol;
- Rect.Bottom := ARow;
- Rect.Right := ACol;
- InvalidateRect(Rect);
- end;
-
- procedure TQDBGrid.InvalidateCol(ACol: Longint);
- var
- Rect: TGridRect;
- begin
- if not HandleAllocated then Exit;
- Rect.Top := 0;
- Rect.Left := ACol;
- Rect.Bottom := VisibleRowCount + 1;
- Rect.Right := ACol;
- InvalidateRect(Rect);
- end;
-
- procedure TQDBGrid.InvalidateRow(ARow: Longint);
- var
- Rect: TGridRect;
- begin
- if not HandleAllocated then Exit;
- Rect.Top := ARow;
- Rect.Left := 0;
- Rect.Bottom := ARow;
- Rect.Right := VisibleColCount + 1;
- InvalidateRect(Rect);
- end;
-
- procedure TQDBGrid.InvalidateGrid;
- begin
- Invalidate;
- end;
-
- procedure TQDBGrid.InvalidateRect(ARect: TGridRect);
- var
- InvalidRect: TRect;
- begin
- if not HandleAllocated then Exit;
- GridRectToScreenRect(ARect, InvalidRect, True);
- Windows.InvalidateRect(Handle, @InvalidRect, False);
- end;
-
- procedure TQDBGrid.ModifyScrollBar(ScrollBar, ScrollCode, Pos: Cardinal);
- var
- NewTopLeft, MaxTopLeft: TGridCoord;
- DrawInfo: TGridDrawInfo;
-
- function Min: Longint;
- begin
- if ScrollBar = SB_HORZ then Result := FixedCols
- else Result := FixedRows;
- end;
-
- function Max: Longint;
- begin
- if ScrollBar = SB_HORZ then Result := MaxTopLeft.X
- else Result := MaxTopLeft.Y;
- end;
-
- function PageUp: Longint;
- var
- MaxTopLeft: TGridCoord;
- begin
- MaxTopLeft := CalcMaxTopLeft(FTopLeft, DrawInfo);
- if ScrollBar = SB_HORZ then
- Result := FTopLeft.X - MaxTopLeft.X else
- Result := FTopLeft.Y - MaxTopLeft.Y;
- if Result < 1 then Result := 1;
- end;
-
- function PageDown: Longint;
- var
- DrawInfo: TGridDrawInfo;
- begin
- CalcDrawInfo(DrawInfo);
- with DrawInfo do
- if ScrollBar = SB_HORZ then
- Result := Horz.LastFullVisibleCell - FTopLeft.X else
- Result := Vert.LastFullVisibleCell - FTopLeft.Y;
- if Result < 1 then Result := 1;
- end;
-
- function CalcScrollBar(Value: Longint): Longint;
- begin
- Result := Value;
- case ScrollCode of
- SB_LINEUP:
- Result := Value - 1;
- SB_LINEDOWN:
- Result := Value + 1;
- SB_PAGEUP:
- Result := Value - PageUp;
- SB_PAGEDOWN:
- Result := Value + PageDown;
- SB_THUMBPOSITION, SB_THUMBTRACK:
- if (goThumbTracking in Options) or (ScrollCode = SB_THUMBPOSITION) then
- Result := Min + LongMulDiv(Pos, Max - Min, MaxShortInt);
- SB_BOTTOM:
- Result := Min;
- SB_TOP:
- Result := Min;
- end;
- end;
-
- procedure ModifyPixelScrollBar(Code, Pos: Cardinal);
- var
- NewOffset: Integer;
- OldOffset: Integer;
- R: TGridRect;
- begin
- NewOffset := FColOffset;
- case Code of
- SB_LINEUP: Dec(NewOffset, Canvas.TextWidth('0'));
- SB_LINEDOWN: Inc(NewOffset, Canvas.TextWidth('0'));
- SB_PAGEUP: Dec(NewOffset, ClientWidth);
- SB_PAGEDOWN: Inc(NewOffset, ClientWidth);
- SB_THUMBPOSITION: NewOffset := Pos;
- SB_THUMBTRACK: if goThumbTracking in Options then NewOffset := Pos;
- SB_BOTTOM: NewOffset := 0;
- SB_TOP: NewOffset := ColWidths[0] - ClientWidth;
- end;
- if NewOffset < 0 then
- NewOffset := 0
- else if NewOffset >= ColWidths[0] - ClientWidth then
- NewOffset := ColWidths[0] - ClientWidth;
- if NewOffset <> FColOffset then
- begin
- OldOffset := FColOffset;
- FColOffset := NewOffset;
- ScrollData(OldOffset - NewOffset, 0);
- FillChar(R, SizeOf(R), 0);
- R.Bottom := FixedRows;
- InvalidateRect(R);
- inherited Update;
- UpdateScrollPos;
- end;
- end;
-
- begin
- if Visible and CanFocus and TabStop and not (csDesigning in ComponentState) then
- SetFocus;
- if (ScrollBar = SB_HORZ) and (ColCount = 1) then
- begin
- ModifyPixelScrollBar(ScrollCode, Pos);
- Exit;
- end;
- CalcDrawInfo(DrawInfo);
- MaxTopLeft.X := ColCount - 1;
- MaxTopLeft.Y := RowCount - 1;
- MaxTopLeft := CalcMaxTopLeft(MaxTopLeft, DrawInfo);
- NewTopLeft := FTopLeft;
- if ScrollBar = SB_HORZ then NewTopLeft.X := CalcScrollBar(NewTopLeft.X)
- else NewTopLeft.Y := CalcScrollBar(NewTopLeft.Y);
- if NewTopLeft.X < FixedCols then NewTopLeft.X := FixedCols
- else if NewTopLeft.X > MaxTopLeft.X then NewTopLeft.X := MaxTopLeft.X;
- if NewTopLeft.Y < FixedRows then NewTopLeft.Y := FixedRows
- else if NewTopLeft.Y > MaxTopLeft.Y then NewTopLeft.Y := MaxTopLeft.Y;
- if (NewTopLeft.X <> FTopLeft.X) or (NewTopLeft.Y <> FTopLeft.Y) then
- MoveTopLeft(NewTopLeft.X, NewTopLeft.Y);
- end;
-
- //<> handles the editing of ftstrings and ftrichstrings
- procedure TQDBGrid.PopupMemo;
- var
- m: TMemoryStream;
- s: string;
- l: integer;
- begin
- with TMemoForm.Create(nil) do
- try
- RichEdit.PlainText := (Columns[FInPlaceCol].FieldType = ftstrings);
- RichEdit.ReadOnly := not ((goEditing in Options) and not QDBItem.ReadOnly and not Columns[Col].ReadOnly);
- QDBItem.ItemIndex := Row - FixedRows;
- QDBItem.Fetch;
- m := QDBItem.GetField(Columns[FInPlaceCol].FieldIndex);
- RichEdit.Lines.LoadFromStream(m);
- m.Position := 0;
- if (mrOk = ShowModal) and RichEdit.Modified then
- begin
- m.Clear;
- RichEdit.Lines.SaveToStream(m);
- m.Position := 0;
- QDBItem.Store;
- // now update the editor too (just 80 chars)
- SetLength(s,80);
- l:=m.Read(s[1],80);
- SetLength(s,l);
- FInPlaceEdit.Text := s;
- m.Position:=0;
- end;
- finally
- Free;
- end;
- end;
-
- //<> handles the editing of ftgraphics
- procedure TQDBGrid.PopupGraphic;
- var
- m: TMemoryStream;
- Ext: array[0..3] of char;
- GraphicClass: TGraphicClass;
- NewGraphic: TGraphic;
- begin
- with TGraphicForm.Create(nil) do
- try
- QDBItem.ItemIndex := Row - FixedRows;
- QDBItem.Fetch;
- m := QDBItem.GetField(Columns[FInPlaceCol].FieldIndex);
- Ext[3] := #0;
- try
- if 3 = m.Read(Ext[0], 3) then
- begin
- GraphicClass := GetGraphicClass(StrPas(Ext));
- if GraphicClass <> nil then
- begin
- NewGraphic := GraphicClass.Create;
- try
- try
- NewGraphic.LoadFromStream(m);
- except
- NewGraphic.Free;
- raise;
- end;
- image.Picture.Graphic:=NewGraphic;
- finally
- NewGraphic.Free;
- end;
- end;
- end;
- except
- end;
- m.Position := 0;
- ImportButton.Enabled := (goEditing in Options) and not QDBItem.ReadOnly and not Columns[Col].ReadOnly;
- if (mrOk = ShowModal) and Modified then
- begin
- m.Clear;
- StrPCopy(Ext, LowerCase(GraphicExtension(TGraphicClass(Image.Picture.Graphic.ClassType))));
- if StrPas(Ext) <> '' then
- begin
- m.WriteBuffer(Ext[0], 3);
- Image.Picture.Graphic.SaveToStream(m);
- m.Position := 0;
- QDBItem.Store;
- end;
- end;
- finally
- Free;
- end;
- end;
-
- //<> handles oridnary edit button clicks
- procedure TQDBGrid.EditButtonClick;
- var
- Value: string;
- begin
- //<> if the Column has a handler use it
- if Assigned(Columns[FInPlaceCol].FOnButtonClick) then
- begin
- value := FInPlaceEdit.text;
- Columns[FInPlaceCol].FOnButtonClick(self, value);
- if not Columns[FInPlaceCol].ReadOnly then
- text := value;
- end
- else
- begin
- //<> next try the built-in editors
- case Columns[FInPlaceCol].FieldType of
- ftstrings, ftrichstrings: PopupMemo;
- ftgraphic: PopupGraphic;
- else
- //<> finally use the grid's click handler
- if Assigned(FOnEditButtonClick) then
- FOnEditButtonClick(self);
- end;
- end;
- KillMessage(Handle, WM_CHAR);
- end;
-
- procedure TQDBGrid.MemoButtonClick;
- var
- Value: string;
- begin
- //<> if the column has a handler it overrides built-in editor
- if Assigned(Columns[FInPlaceCol].FOnButtonClick) then
- begin
- value := FInPlaceEdit.text;
- Columns[FInPlaceCol].FOnButtonClick(self, value);
- if not Columns[FInPlaceCol].ReadOnly then
- text := value;
- end
- else
- begin
- PopupMemo;
- end;
- KillMessage(Handle, WM_CHAR);
- end;
-
- procedure TQDBGrid.GraphicButtonClick;
- var
- Value: string;
- begin
- //<> if the column has a handler it overrides built-in editor
- if Assigned(Columns[FInPlaceCol].FOnButtonClick) then
- begin
- value := FInPlaceEdit.text;
- Columns[FInPlaceCol].FOnButtonClick(self, value);
- if not Columns[FInPlaceCol].ReadOnly then
- text := value;
- end
- else
- begin
- PopupGraphic;
- end;
- KillMessage(Handle, WM_CHAR);
- end;
-
- procedure TQDBGrid.BooleanButtonClick;
- var
- Value: string;
- begin
- //<> if the column has a handler it overrides built-in editor
- if Assigned(Columns[FInPlaceCol].FOnButtonClick) then
- begin
- value := FInPlaceEdit.text;
- Columns[FInPlaceCol].FOnButtonClick(self, value);
- if not Columns[FInPlaceCol].ReadOnly then
- text := value;
- end
- else
- begin
- //<> built-in behavior -- swap glyphs (this Marlett font)
- if Cells[Col, Row] = chr(97) then
- Cells[Col, Row] := chr(114)
- else
- Cells[Col, Row] := chr(97);
- end;
- KillMessage(Handle, WM_CHAR);
- end;
-
- procedure TQDBGrid.MoveAdjust(var CellPos: Longint; FromIndex, ToIndex: Longint);
- var
- Min, Max: Longint;
- begin
- if CellPos = FromIndex then CellPos := ToIndex
- else
- begin
- Min := FromIndex;
- Max := ToIndex;
- if FromIndex > ToIndex then
- begin
- Min := ToIndex;
- Max := FromIndex;
- end;
- if (CellPos >= Min) and (CellPos <= Max) then
- if FromIndex > ToIndex then
- Inc(CellPos) else
- Dec(CellPos);
- end;
- end;
-
- procedure TQDBGrid.MoveAnchor(const NewAnchor: TGridCoord);
- begin
- MoveCurrent(NewAnchor.X, NewAnchor.Y, True, True);
- end;
-
- procedure TQDBGrid.MoveCurrent(ACol, ARow: Longint; MoveAnchor,
- Show: Boolean);
- var
- OldSel: TGridRect;
- OldCurrent: TGridCoord;
- begin
- if (ACol < 0) or (ARow < 0) or (ACol >= ColCount) or (ARow >= RowCount) then
- InvalidOp(SIndexOutOfRange);
- if SelectCell(ACol, ARow) then
- begin
- OldSel := Selection;
- OldCurrent := FCurrent;
- FCurrent.X := ACol;
- FCurrent.Y := ARow;
- if not (goAlwaysShowEditor in Options) then HideEditor;
- FAnchor := FCurrent;
- if Show then ClampInView(FCurrent);
- SelectionMoved(OldSel);
- with OldCurrent do InvalidateCell(X, Y);
- with FCurrent do InvalidateCell(ACol, ARow);
- end;
- end;
-
- procedure TQDBGrid.MoveTopLeft(ALeft, ATop: Longint);
- var
- OldTopLeft: TGridCoord;
- begin
- if (ALeft = FTopLeft.X) and (ATop = FTopLeft.Y) then Exit;
- inherited Update;
- OldTopLeft := FTopLeft;
- FTopLeft.X := ALeft;
- FTopLeft.Y := ATop;
- TopLeftMoved(OldTopLeft);
- end;
-
- procedure TQDBGrid.ResizeCol(Index: Longint; OldSize, NewSize: Integer);
- begin
- InvalidateGrid;
- end;
-
- procedure TQDBGrid.ResizeRow(Index: Longint; OldSize, NewSize: Integer);
- begin
- InvalidateGrid;
- end;
-
- procedure TQDBGrid.SelectionMoved(const OldSel: TGridRect);
- var
- OldRect, NewRect: TRect;
- AXorRects: TXorRects;
- I: Integer;
- begin
- if not HandleAllocated then Exit;
- GridRectToScreenRect(OldSel, OldRect, True);
- GridRectToScreenRect(Selection, NewRect, True);
- XorRects(OldRect, NewRect, AXorRects);
- for I := Low(AXorRects) to High(AXorRects) do
- Windows.InvalidateRect(Handle, @AXorRects[I], False);
- end;
-
- procedure TQDBGrid.ScrollDataInfo(DX, DY: Integer;
- var DrawInfo: TGridDrawInfo);
- var
- ScrollArea: TRect;
- ScrollFlags: Integer;
- begin
- with DrawInfo do
- begin
- ScrollFlags := SW_INVALIDATE;
- { Scroll the area }
- if DY = 0 then
- begin
- { Scroll both the column titles and data area at the same time }
- ScrollArea := Rect(Horz.FixedBoundary, 0, Horz.GridExtent, Vert.GridExtent);
- ScrollWindowEx(Handle, DX, 0, @ScrollArea, @ScrollArea, 0, nil, ScrollFlags);
- end
- else if DX = 0 then
- begin
- { Scroll both the row titles and data area at the same time }
- ScrollArea := Rect(0, Vert.FixedBoundary, Horz.GridExtent, Vert.GridExtent);
- ScrollWindowEx(Handle, 0, DY, @ScrollArea, @ScrollArea, 0, nil, ScrollFlags);
- end
- else
- begin
- { Scroll titles and data area separately }
- { Column titles }
- ScrollArea := Rect(Horz.FixedBoundary, 0, Horz.GridExtent, Vert.FixedBoundary);
- ScrollWindowEx(Handle, DX, 0, @ScrollArea, @ScrollArea, 0, nil, ScrollFlags);
- { Row titles }
- ScrollArea := Rect(0, Vert.FixedBoundary, Horz.FixedBoundary, Vert.GridExtent);
- ScrollWindowEx(Handle, 0, DY, @ScrollArea, @ScrollArea, 0, nil, ScrollFlags);
- { Data area }
- ScrollArea := Rect(Horz.FixedBoundary, Vert.FixedBoundary, Horz.GridExtent,
- Vert.GridExtent);
- ScrollWindowEx(Handle, DX, DY, @ScrollArea, @ScrollArea, 0, nil, ScrollFlags);
- end;
- end;
- end;
-
- procedure TQDBGrid.ScrollData(DX, DY: Integer);
- var
- DrawInfo: TGridDrawInfo;
- begin
- CalcDrawInfo(DrawInfo);
- ScrollDataInfo(DX, DY, DrawInfo);
- end;
-
- procedure TQDBGrid.TopLeftMoved(const OldTopLeft: TGridCoord);
-
- function CalcScroll(const AxisInfo: TGridAxisDrawInfo;
- OldPos, CurrentPos: Integer; var Amount: Longint): Boolean;
- var
- Start, Stop: Longint;
- I: Longint;
- begin
- Result := False;
- with AxisInfo do
- begin
- if OldPos < CurrentPos then
- begin
- Start := OldPos;
- Stop := CurrentPos;
- end
- else
- begin
- Start := CurrentPos;
- Stop := OldPos;
- end;
- Amount := 0;
- for I := Start to Stop - 1 do
- begin
- Inc(Amount, GetExtent(I) + EffectiveLineWidth);
- if Amount > (GridBoundary - FixedBoundary) then
- begin
- { Scroll amount too big, redraw the whole thing }
- InvalidateGrid;
- Exit;
- end;
- end;
- if OldPos < CurrentPos then Amount := -Amount;
- end;
- Result := True;
- end;
-
- var
- DrawInfo: TGridDrawInfo;
- Delta: TGridCoord;
- n: longint;
- diff: longint;
- z: TStringSparseList;
- begin
- //<> major stuff going on here
- { the following messy code handles the clearing and loading of data }
- { in response to which rows are visible -- all it really does is load }
- { rows which are just coming in to view and clear the storage for rows }
- { which have just disappeared. This saves a LOT of memory. }
- diff := FTopLeft.Y - OldTopLeft.Y;
- if diff > 0 then
- begin
- { trim the displacement to the number of visible rows }
- if diff > VisibleRowCount then
- diff := VisibleRowCount;
- { clear rows going off the top }
- for n := OldTopLeft.Y to OldTopLeft.Y + diff - 1 do
- begin
- z := TStringSparseList(TSparseList(FData)[n]);
- if Assigned(z) then z.Clear;
- InvalidateRow(n);
- end;
- { load rows appearing at the bottom }
- for n := FTopLeft.Y + VisibleRowCount - diff to FTopLeft.Y + VisibleRowCount - 1 do
- LoadRow(n);
- end
- else
- begin
- { and vice versa }
- diff := abs(diff);
- if diff > VisibleRowCount then
- diff := VisibleRowCount;
- for n := OldTopLeft.Y + VisibleRowCount - diff to OldTopLeft.Y + VisibleRowCount - 1 do
- begin
- z := TStringSparseList(TSparseList(FData)[n]);
- if Assigned(z) then z.Clear;
- InvalidateRow(n);
- end;
- for n := FTopLeft.Y to FTopLeft.Y + diff - 1 do
- LoadRow(n);
- end;
- { rest is unchanged }
- UpdateScrollPos;
- CalcDrawInfo(DrawInfo);
- if CalcScroll(DrawInfo.Horz, OldTopLeft.X, FTopLeft.X, Delta.X) and
- CalcScroll(DrawInfo.Vert, OldTopLeft.Y, FTopLeft.Y, Delta.Y) then
- ScrollDataInfo(Delta.X, Delta.Y, DrawInfo);
- TopLeftChanged;
- end;
-
- procedure TQDBGrid.UpdateScrollPos;
- var
- DrawInfo: TGridDrawInfo;
- MaxTopLeft: TGridCoord;
-
- procedure SetScroll(Code: Word; Value: Integer);
- begin
- if GetScrollPos(Handle, Code) <> Value then
- SetScrollPos(Handle, Code, Value, True);
- end;
-
- begin
- if (not HandleAllocated) or (ScrollBars = ssNone) then Exit;
- CalcDrawInfo(DrawInfo);
- MaxTopLeft.X := ColCount - 1;
- MaxTopLeft.Y := RowCount - 1;
- MaxTopLeft := CalcMaxTopLeft(MaxTopLeft, DrawInfo);
- if ScrollBars in [ssHorizontal, ssBoth] then
- if ColCount = 1 then
- begin
- if (FColOffset > 0) and (ClientWidth > ColWidths[0] - FColOffset) then
- ModifyScrollbar(SB_HORZ, SB_THUMBPOSITION, ColWidths[0] - ClientWidth)
- else
- SetScroll(SB_HORZ, FColOffset)
- end
- else
- SetScroll(SB_HORZ, LongMulDiv(FTopLeft.X - FixedCols, MaxShortInt,
- MaxTopLeft.X - FixedCols));
- if ScrollBars in [ssVertical, ssBoth] then
- SetScroll(SB_VERT, LongMulDiv(FTopLeft.Y - FixedRows, MaxShortInt,
- MaxTopLeft.Y - FixedRows));
- end;
-
- procedure TQDBGrid.UpdateScrollRange;
- var
- MaxTopLeft, OldTopLeft: TGridCoord;
- DrawInfo: TGridDrawInfo;
- OldScrollBars: TScrollStyle;
- Updated: Boolean;
-
- procedure DoUpdate;
- begin
- if not Updated then
- begin
- inherited Update;
- Updated := True;
- end;
- end;
-
- function ScrollBarVisible(Code: Word): Boolean;
- var
- Min, Max: Integer;
- begin
- Result := False;
- if (ScrollBars = ssBoth) or
- ((Code = SB_HORZ) and (ScrollBars = ssHorizontal)) or
- ((Code = SB_VERT) and (ScrollBars = ssVertical)) then
- begin
- GetScrollRange(Handle, Code, Min, Max);
- Result := Min <> Max;
- end;
- end;
-
- procedure CalcSizeInfo;
- begin
- CalcDrawInfoXY(DrawInfo, DrawInfo.Horz.GridExtent, DrawInfo.Vert.GridExtent);
- MaxTopLeft.X := ColCount - 1;
- MaxTopLeft.Y := RowCount - 1;
- MaxTopLeft := CalcMaxTopLeft(MaxTopLeft, DrawInfo);
- end;
-
- procedure SetAxisRange(var Max, Old, Current: Longint; Code: Word;
- Fixeds: Integer);
- begin
- CalcSizeInfo;
- if Fixeds < Max then
- SetScrollRange(Handle, Code, 0, MaxShortInt, True)
- else
- SetScrollRange(Handle, Code, 0, 0, True);
- if Old > Max then
- begin
- DoUpdate;
- Current := Max;
- end;
- end;
-
- procedure SetHorzRange;
- var
- Range: Integer;
- begin
- if OldScrollBars in [ssHorizontal, ssBoth] then
- if ColCount = 1 then
- begin
- Range := ColWidths[0] - ClientWidth;
- if Range < 0 then Range := 0;
- SetScrollRange(Handle, SB_HORZ, 0, Range, True);
- end
- else
- SetAxisRange(MaxTopLeft.X, OldTopLeft.X, FTopLeft.X, SB_HORZ, FixedCols);
- end;
-
- procedure SetVertRange;
- begin
- if OldScrollBars in [ssVertical, ssBoth] then
- SetAxisRange(MaxTopLeft.Y, OldTopLeft.Y, FTopLeft.Y, SB_VERT, FixedRows);
- end;
-
- begin
- if (ScrollBars = ssNone) or not HandleAllocated then Exit;
- with DrawInfo do
- begin
- Horz.GridExtent := ClientWidth;
- Vert.GridExtent := ClientHeight;
- { Ignore scroll bars for initial calculation }
- if ScrollBarVisible(SB_HORZ) then
- Inc(Vert.GridExtent, GetSystemMetrics(SM_CYHSCROLL));
- if ScrollBarVisible(SB_VERT) then
- Inc(Horz.GridExtent, GetSystemMetrics(SM_CXVSCROLL));
- end;
- OldTopLeft := FTopLeft;
- { Temporarily mark us as not having scroll bars to avoid recursion }
- OldScrollBars := FScrollBars;
- FScrollBars := ssNone;
- Updated := False;
- try
- { Update scrollbars }
- SetHorzRange;
- DrawInfo.Vert.GridExtent := ClientHeight;
- SetVertRange;
- if DrawInfo.Horz.GridExtent <> ClientWidth then
- begin
- DrawInfo.Horz.GridExtent := ClientWidth;
- SetHorzRange;
- end;
- finally
- FScrollBars := OldScrollBars;
- end;
- UpdateScrollPos;
- if (FTopLeft.X <> OldTopLeft.X) or (FTopLeft.Y <> OldTopLeft.Y) then
- TopLeftMoved(OldTopLeft);
- end;
-
- function TQDBGrid.CreateEditor: TQDBGridInplaceEdit;
- begin
- Result := TQDBGridInplaceEdit.Create(Self);
- end;
-
- procedure TQDBGrid.CreateParams(var Params: TCreateParams);
- begin
- inherited CreateParams(Params);
- with Params do
- begin
- Style := Style or WS_TABSTOP;
- if FScrollBars in [ssVertical, ssBoth] then Style := Style or WS_VSCROLL;
- if FScrollBars in [ssHorizontal, ssBoth] then Style := Style or WS_HSCROLL;
- WindowClass.style := CS_DBLCLKS;
- if FBorderStyle = bsSingle then
- if NewStyleControls and Ctl3D then
- begin
- Style := Style and not WS_BORDER;
- ExStyle := ExStyle or WS_EX_CLIENTEDGE;
- end
- else
- Style := Style or WS_BORDER;
- end;
- end;
-
- procedure TQDBGrid.KeyDown(var Key: Word; Shift: TShiftState);
- var
- NewTopLeft, NewCurrent, MaxTopLeft: TGridCoord;
- DrawInfo: TGridDrawInfo;
- PageWidth, PageHeight: Integer;
-
- procedure CalcPageExtents;
- begin
- CalcDrawInfo(DrawInfo);
- PageWidth := DrawInfo.Horz.LastFullVisibleCell - LeftCol;
- if PageWidth < 1 then PageWidth := 1;
- PageHeight := DrawInfo.Vert.LastFullVisibleCell - TopRow;
- if PageHeight < 1 then PageHeight := 1;
- end;
-
- procedure Restrict(var Coord: TGridCoord; MinX, MinY, MaxX, MaxY: Longint);
- begin
- with Coord do
- begin
- if X > MaxX then X := MaxX
- else if X < MinX then X := MinX;
- if Y > MaxY then Y := MaxY
- else if Y < MinY then Y := MinY;
- end;
- end;
-
- begin
- inherited KeyDown(Key, Shift);
- if not CanGridAcceptKey(Key, Shift) then Key := 0;
- NewCurrent := FCurrent;
- NewTopLeft := FTopLeft;
- CalcPageExtents;
- if ssCtrl in Shift then
- case Key of
- VK_UP: Dec(NewTopLeft.Y);
- VK_DOWN: Inc(NewTopLeft.Y);
- VK_LEFT:
- begin
- Dec(NewCurrent.X, PageWidth);
- Dec(NewTopLeft.X, PageWidth);
- end;
- VK_RIGHT:
- begin
- Inc(NewCurrent.X, PageWidth);
- Inc(NewTopLeft.X, PageWidth);
- end;
- VK_PRIOR: NewCurrent.Y := TopRow;
- VK_NEXT: NewCurrent.Y := DrawInfo.Vert.LastFullVisibleCell;
- VK_HOME:
- begin
- NewCurrent.X := FixedCols;
- NewCurrent.Y := FixedRows;
- end;
- VK_END:
- begin
- NewCurrent.X := ColCount - 1;
- NewCurrent.Y := RowCount - 1;
- end;
- //<> delete a row
- VK_DELETE:
- begin
- if goAllowDelete in Options then
- DeleteARow(Row);
- end;
- //<> add a row
- VK_INSERT:
- begin
- if (not Adding) and (goAllowAdd in Options) then
- AddARow;
- end;
- end
- else
- case Key of
- VK_UP: Dec(NewCurrent.Y);
- VK_DOWN: Inc(NewCurrent.Y);
- VK_LEFT: Dec(NewCurrent.X);
- VK_RIGHT: Inc(NewCurrent.X);
- VK_NEXT:
- begin
- Inc(NewCurrent.Y, PageHeight);
- Inc(NewTopLeft.Y, PageHeight);
- end;
- VK_PRIOR:
- begin
- Dec(NewCurrent.Y, PageHeight);
- Dec(NewTopLeft.Y, PageHeight);
- end;
- VK_HOME: NewCurrent.X := FixedCols;
- VK_END: NewCurrent.X := ColCount - 1;
- VK_TAB:
- if not (ssAlt in Shift) then
- repeat
- if ssShift in Shift then
- begin
- Dec(NewCurrent.X);
- if NewCurrent.X < FixedCols then
- begin
- NewCurrent.X := ColCount - 1;
- Dec(NewCurrent.Y);
- if NewCurrent.Y < FixedRows then NewCurrent.Y := RowCount - 1;
- end;
- Shift := [];
- end
- else
- begin
- Inc(NewCurrent.X);
- if NewCurrent.X >= ColCount then
- begin
- NewCurrent.X := FixedCols;
- Inc(NewCurrent.Y);
- if NewCurrent.Y >= RowCount then NewCurrent.Y := FixedRows;
- end;
- end;
- until TabStops[NewCurrent.X] or (NewCurrent.X = FCurrent.X);
- VK_F2: EditorMode := True;
- end;
- MaxTopLeft.X := ColCount - 1;
- MaxTopLeft.Y := RowCount - 1;
- MaxTopLeft := CalcMaxTopLeft(MaxTopLeft, DrawInfo);
- Restrict(NewTopLeft, FixedCols, FixedRows, MaxTopLeft.X, MaxTopLeft.Y);
- if (NewTopLeft.X <> LeftCol) or (NewTopLeft.Y <> TopRow) then
- MoveTopLeft(NewTopLeft.X, NewTopLeft.Y);
- Restrict(NewCurrent, FixedCols, FixedRows, ColCount - 1, RowCount - 1);
- if (NewCurrent.X <> Col) or (NewCurrent.Y <> Row) then
- FocusCell(NewCurrent.X, NewCurrent.Y, not (ssShift in Shift));
- end;
-
- procedure TQDBGrid.KeyPress(var Key: Char);
- begin
- inherited KeyPress(Key);
- if not (goAlwaysShowEditor in Options) and (Key = #13) then
- begin
- if FEditorMode then
- HideEditor else
- ShowEditor;
- Key := #0;
- end;
- end;
-
- //<> new routine
-
- procedure TQDBGrid.DeleteARow(ARow: integer);
- begin
- if assigned(FBeforeDelete) then FBeforeDelete(self);
- FQDBItem.ItemIndex := ARow - FixedRows;
- FQDBItem.Delete;
- RowCount := RowCount - 1;
- ReLoad;
- end;
-
- //<> new routine
-
- procedure TQDBGrid.AddARow;
- var
- tempkey: TKey;
- begin
- if assigned(FBeforeInsert) then FBeforeInsert(self);
- FQDBItem.ItemIndex := Row - FixedRows;
- { get the key of the current item }
- tempkey := FQDBItem.Key;
- { generate a new key that fits here }
- tempkey[length(tempkey)] := #1;
- FQDBItem.Insert;
- FQDBItem.StoreAs(tempkey);
- FQDBItem.Cancel;
- RowCount := RowCount + 1;
- ReLoad;
- Adding := true;
- end;
-
- procedure TQDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer);
- var
- CellHit: TGridCoord;
- DrawInfo: TGridDrawInfo;
- MoveDrawn: Boolean;
- n: integer;
- begin
- MoveDrawn := False;
- try
- HideEdit;
- except
- //<> the edit failed so we respond in some way
- if Assigned(Columns[Col].FOnInvalidValue) then
- Columns[Col].FOnInvalidvalue(self)
- else
- MessageBeep(0);
- Exit;
- end;
- if not (csDesigning in ComponentState) and CanFocus then
- begin
- SetFocus;
- if ValidParentForm(Self).ActiveControl <> Self then
- begin
- MouseCapture := False;
- Exit;
- end;
- end;
- //<> check for double left clicks
- if (Button = mbLeft) and (ssDouble in Shift) then
- DblClick
- //<> handle ordinary left clicks
- else if Button = mbLeft then
- begin
- CalcDrawInfo(DrawInfo);
- CalcSizingState(X, Y, FGridState, FSizingIndex, FSizingPos, FSizingOfs,
- DrawInfo);
- if FGridState <> gsNormal then
- begin
- DrawSizingLine(DrawInfo);
- Exit;
- end;
- CellHit := CalcCoordFromPoint(X, Y, DrawInfo);
- if (CellHit.X >= FixedCols) and (CellHit.Y >= FixedRows) then
- begin
- if goEditing in Options then
- begin
- if (CellHit.X = FCurrent.X) and (CellHit.Y = FCurrent.Y) then
- ShowEditor
- else
- begin
- MoveCurrent(CellHit.X, CellHit.Y, True, True);
- UpdateEdit;
- end;
- Click;
- end
- else
- begin
- FGridState := gsSelecting;
- SetTimer(Handle, 1, 60, nil);
- if ssShift in Shift then
- MoveAnchor(CellHit)
- else
- MoveCurrent(CellHit.X, CellHit.Y, True, True);
- end;
- end
- //<> handle ctrl-left-click
- else if (ssCtrl in Shift) and (CellHit.Y = 0) and (goSelectColumns in Options) then
- begin
- if (ssShift in Shift) then
- begin
- //<> i.e. ctrl-shift-click -- unselect all columns
- for n:= FixedCols to Columns.Count-1 do
- begin
- Columns[n].Selected := false;
- InvalidateCol(n);
- end;
- end
- else
- //<> ctrl-left-click -- select this columns
- begin
- Columns[CellHit.X].Selected := not Columns[CellHit.X].Selected;
- InvalidateCol(CellHit.X);
- end;
- end
- // check for clicks in the header
- else if (CellHit.Y = 0) then
- begin
- if Assigned(FOnHeaderClick) then
- FOnHeaderClick(Self, CellHit.X);
- end
- end;
- try
- inherited MouseDown(Button, Shift, X, Y);
- except
- if MoveDrawn then DrawMove;
- end;
- end;
-
- procedure TQDBGrid.MouseMove(Shift: TShiftState; X, Y: Integer);
- var
- DrawInfo: TGridDrawInfo;
- CellHit: TGridCoord;
- begin
- CalcDrawInfo(DrawInfo);
- case FGridState of
- gsSelecting, gsColMoving:
- begin
- CellHit := CalcCoordFromPoint(X, Y, DrawInfo);
- if (CellHit.X >= FixedCols) and (CellHit.Y >= FixedRows) and
- (CellHit.X <= DrawInfo.Horz.LastFullVisibleCell + 1) and
- (CellHit.Y <= DrawInfo.Vert.LastFullVisibleCell + 1) then
- case FGridState of
- gsSelecting:
- if ((CellHit.X <> FAnchor.X) or (CellHit.Y <> FAnchor.Y)) then
- MoveAnchor(CellHit);
- gsColMoving:
- MoveAndScroll(X, CellHit.X, DrawInfo, DrawInfo.Horz, SB_HORZ);
- end;
- end;
- gsRowSizing, gsColSizing:
- begin
- DrawSizingLine(DrawInfo); { XOR it out }
- if FGridState = gsRowSizing then
- FSizingPos := Y + FSizingOfs else
- FSizingPos := X + FSizingOfs;
- DrawSizingLine(DrawInfo); { XOR it back in }
- end;
- end;
- inherited MouseMove(Shift, X, Y);
- end;
-
- procedure TQDBGrid.MouseUp(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer);
- var
- DrawInfo: TGridDrawInfo;
- NewSize: Integer;
-
- function ResizeLine(const AxisInfo: TGridAxisDrawInfo): Integer;
- var
- I: Integer;
- begin
- with AxisInfo do
- begin
- Result := FixedBoundary;
- for I := FirstGridCell to FSizingIndex - 1 do
- Inc(Result, GetExtent(I) + EffectiveLineWidth);
- Result := FSizingPos - Result;
- end;
- end;
-
- begin
- try
- case FGridState of
- gsSelecting:
- begin
- MouseMove(Shift, X, Y);
- KillTimer(Handle, 1);
- UpdateEdit;
- Click;
- end;
- gsRowSizing, gsColSizing:
- begin
- CalcDrawInfo(DrawInfo);
- DrawSizingLine(DrawInfo);
- if FGridState = gsColSizing then
- begin
- NewSize := ResizeLine(DrawInfo.Horz);
- if NewSize > 1 then
- begin
- ColWidths[FSizingIndex] := NewSize;
- UpdateDesigner;
- end;
- end
- else
- begin
- NewSize := ResizeLine(DrawInfo.Vert);
- if NewSize > 1 then
- begin
- RowHeights[FSizingIndex] := NewSize;
- UpdateDesigner;
- end;
- end;
- end;
- gsColMoving:
- begin
- DrawMove;
- KillTimer(Handle, 1);
- if FMoveIndex <> FMovePos then
- begin
- if FGridState = gsColMoving then
- MoveColumn(FMoveIndex, FMovePos)
- else
- MoveRow(FMoveIndex, FMovePos);
- UpdateDesigner;
- end;
- UpdateEdit;
- end;
- else
- UpdateEdit;
- end;
- inherited MouseUp(Button, Shift, X, Y);
- finally
- FGridState := gsNormal;
- end;
- end;
-
- procedure TQDBGrid.MoveAndScroll(Mouse, CellHit: Integer;
- var DrawInfo: TGridDrawInfo; var Axis: TGridAxisDrawInfo; ScrollBar: Integer);
- begin
- if (CellHit <> FMovePos) and
- not ((FMovePos = Axis.FixedCellCount) and (Mouse < Axis.FixedBoundary)) and
- not ((FMovePos = Axis.GridCellCount - 1) and (Mouse > Axis.GridBoundary)) then
- begin
- DrawMove;
- if (Mouse < Axis.FixedBoundary) then
- begin
- if (FMovePos > Axis.FixedCellCount) then
- begin
- ModifyScrollbar(ScrollBar, SB_LINEUP, 0);
- inherited Update;
- CalcDrawInfo(DrawInfo); { this changes contents of Axis var }
- end;
- CellHit := Axis.FirstGridCell;
- end
- else if (Mouse >= Axis.FullVisBoundary) then
- begin
- if (FMovePos = Axis.LastFullVisibleCell) and
- (FMovePos < Axis.GridCellCount - 1) then
- begin
- ModifyScrollBar(Scrollbar, SB_LINEDOWN, 0);
- inherited Update;
- CalcDrawInfo(DrawInfo); { this changes contents of Axis var }
- end;
- CellHit := Axis.LastFullVisibleCell;
- end
- else if CellHit < 0 then CellHit := FMovePos;
- FMovePos := CellHit;
- DrawMove;
- end;
- end;
-
- //<> whenba new file is set in QDBItem we have to load it's structure
- procedure TQDBGrid.FileAssigned(Sender: TObject);
- begin
- if not (csDesigning in ComponentState) then
- begin
- FQDBItem.FetchStructure;
- LoadFieldStructure;
- Load;
- end;
- end;
-
- function TQDBGrid.GetColWidths(Index: Longint): Integer;
- begin
- if (FColWidths = nil) or (Index >= ColCount) then
- Result := DefaultColWidth
- else
- Result := PIntArray(FColWidths)^[Index + 1];
- end;
-
- //<>
- function TQDBGrid.GetQDBItem: TQDBItem;
- begin
- Result := FQDBItem;
- end;
-
- function TQDBGrid.GetRowHeights(Index: Longint): Integer;
- begin
- if (FRowHeights = nil) or (Index >= RowCount) then
- if Index = 0 then
- //<> size the title row to fit the title font
- Result := abs(TitleFont.Height) + FontHeightMargin
- else
- Result := DefaultRowHeight
- else
- Result := PIntArray(FRowHeights)^[Index + 1];
- end;
-
- function TQDBGrid.GetGridWidth: Integer;
- var
- DrawInfo: TGridDrawInfo;
- begin
- CalcDrawInfo(DrawInfo);
- Result := DrawInfo.Horz.GridBoundary;
- end;
-
- function TQDBGrid.GetGridHeight: Integer;
- var
- DrawInfo: TGridDrawInfo;
- begin
- CalcDrawInfo(DrawInfo);
- Result := DrawInfo.Vert.GridBoundary;
- end;
-
- function TQDBGrid.GetSelection: TGridRect;
- begin
- Result := GridRect(FCurrent, FAnchor);
- end;
-
- function TQDBGrid.GetTabStops(Index: Longint): Boolean;
- begin
- if FTabStops = nil then Result := True
- else Result := Boolean(PIntArray(FTabStops)^[Index + 1]);
- end;
-
- function TQDBGrid.GetVisibleColCount: Integer;
- var
- DrawInfo: TGridDrawInfo;
- begin
- CalcDrawInfo(DrawInfo);
- Result := DrawInfo.Horz.LastFullVisibleCell - LeftCol + 1;
- end;
-
- function TQDBGrid.GetVisibleRowCount: Integer;
- var
- DrawInfo: TGridDrawInfo;
- begin
- CalcDrawInfo(DrawInfo);
- Result := DrawInfo.Vert.LastFullVisibleCell - TopRow + 1;
- end;
-
- procedure TQDBGrid.SetBorderStyle(Value: TBorderStyle);
- begin
- if FBorderStyle <> Value then
- begin
- FBorderStyle := Value;
- RecreateWnd;
- end;
- end;
-
- procedure TQDBGrid.SetCol(Value: Longint);
- begin
- if Col <> Value then FocusCell(Value, Row, True);
- end;
-
- procedure TQDBGrid.SetColCount(Value: Longint);
- begin
- if FColCount <> Value then
- begin
- if Value < FixedCols + 1 then Value := FixedCols + 1;
- ChangeSize(Value, RowCount);
- end;
- end;
-
- //<> keep Columns in sync with ColCount
- procedure TQDBGrid.SetColumnCount(NewCount: LongInt);
- begin
- while Columns.count > NewCount do Columns[Columns.Count - 1].destroy;
- while Columns.count < NewCount do Columns.add;
- end;
-
- procedure TQDBGrid.SetColWidths(Index: Longint; Value: Integer);
- begin
- if Index < FixedCols then
- value := FixedColWidth;
- if FColWidths = nil then
- UpdateExtents(FColWidths, ColCount, DefaultColWidth);
- if Index >= ColCount then InvalidOp(SIndexOutOfRange);
- if Value <> PIntArray(FColWidths)^[Index + 1] then
- begin
- ResizeCol(Index, PIntArray(FColWidths)^[Index + 1], Value);
- PIntArray(FColWidths)^[Index + 1] := Value;
- ColWidthsChanged;
- end;
- end;
-
- procedure TQDBGrid.SetDefaultColWidth(Value: Integer);
- begin
- if FColWidths <> nil then UpdateExtents(FColWidths, 0, 0);
- FDefaultColWidth := Value;
- ColWidthsChanged;
- InvalidateGrid;
- end;
-
- procedure TQDBGrid.SetDefaultRowHeight(Value: Integer);
- begin
- if FRowHeights <> nil then UpdateExtents(FRowHeights, 0, 0);
- FDefaultRowHeight := Value;
- RowHeightsChanged;
- InvalidateGrid;
- end;
-
- procedure TQDBGrid.SetFixedColor(Value: TColor);
- begin
- if FFixedColor <> Value then
- begin
- FFixedColor := Value;
- InvalidateGrid;
- end;
- end;
-
- procedure TQDBGrid.SetEditorMode(Value: Boolean);
- begin
- if not Value then
- HideEditor
- else
- begin
- ShowEditor;
- if FInplaceEdit <> nil then FInplaceEdit.Deselect;
- end;
- end;
-
- procedure TQDBGrid.SetGridLineWidth(Value: Integer);
- begin
- if FGridLineWidth <> Value then
- begin
- FGridLineWidth := Value;
- InvalidateGrid;
- end;
- end;
-
- procedure TQDBGrid.SetLeftCol(Value: Longint);
- begin
- if FTopLeft.X <> Value then MoveTopLeft(Value, TopRow);
- end;
-
- procedure TQDBGrid.SetOptions(Value: TGridOptions);
- begin
- if FOptions <> Value then
- begin
- FOptions := Value;
- if not FEditorMode then
- if goAlwaysShowEditor in Value then
- ShowEditor else
- HideEditor;
- InvalidateGrid;
- end;
- end;
-
- //<>
- procedure TQDBGrid.SetQDBItem(Value: TQDBItem);
- begin
- FQDBItem := Value;
- if Value <> nil then
- begin
- FQDBItem.OnFileAssigned := FileAssigned;
- if FQDBItem.Ready then FileAssigned(self);
- Value.FreeNotification(Self);
- end;
- end;
-
- procedure TQDBGrid.SetRow(Value: Longint);
- begin
- if Row <> Value then FocusCell(Col, Value, True);
- end;
-
- procedure TQDBGrid.SetRowCount(Value: Longint);
- begin
- if FRowCount <> Value then
- begin
- if Value < FixedRows + 1 then Value := FixedRows + 1;
- ChangeSize(ColCount, Value);
- end;
- end;
-
- procedure TQDBGrid.SetRowHeights(Index: Longint; Value: Integer);
- begin
- if FRowHeights = nil then
- UpdateExtents(FRowHeights, RowCount, DefaultRowHeight);
- if Index >= RowCount then InvalidOp(SIndexOutOfRange);
- if Value <> PIntArray(FRowHeights)^[Index + 1] then
- begin
- ResizeRow(Index, PIntArray(FRowHeights)^[Index + 1], Value);
- PIntArray(FRowHeights)^[Index + 1] := Value;
- RowHeightsChanged;
- end;
- end;
-
- procedure TQDBGrid.SetScrollBars(Value: TScrollStyle);
- begin
- if FScrollBars <> Value then
- begin
- FScrollBars := Value;
- RecreateWnd;
- end;
- end;
-
- procedure TQDBGrid.SetSelection(Value: TGridRect);
- var
- OldSel: TGridRect;
- begin
- OldSel := Selection;
- FAnchor := Value.TopLeft;
- FCurrent := Value.BottomRight;
- SelectionMoved(OldSel);
- end;
-
- procedure TQDBGrid.SetTabStops(Index: Longint; Value: Boolean);
- begin
- if FTabStops = nil then
- UpdateExtents(FTabStops, ColCount, Integer(True));
- if Index >= ColCount then InvalidOp(SIndexOutOfRange);
- PIntArray(FTabStops)^[Index + 1] := Integer(Value);
- end;
-
- procedure TQDBGrid.SetTopRow(Value: Longint);
- begin
- if FTopLeft.Y <> Value then MoveTopLeft(LeftCol, Value);
- end;
-
- //<> this is where the editing is stored away to file
- procedure TQDBGrid.HideEdit;
- begin
- try
- // note that the edit might fail if invalid ...
- if (FInplaceEdit <> nil) and not busy then
- begin
- if Assigned(FInPlaceEdit) and (FInPlaceCol <> -1) then
- begin
- if not Adding then
- SaveCell(FInPlaceCol, FInPlaceRow);
- end;
- UpdateText;
- FInplaceCol := -1;
- FInplaceRow := -1;
- FInplaceEdit.Hide;
- end;
- except
- // .. so we pass on the exception
- busy := true;
- Col := FInPlaceCol;
- Row := FInPlaceRow;
- busy := false;
- raise
- end;
- end;
-
- procedure TQDBGrid.UpdateEdit;
-
- procedure UpdateEditor;
- begin
- FInplaceCol := Col;
- FInplaceRow := Row;
- FInPlaceEdit.Font := Columns[Col].Font;
- FInplaceEdit.UpdateContents;
- if FInplaceEdit.MaxLength = -1 then FCanEditModify := False
- else FCanEditModify := True;
- FInplaceEdit.SelectAll;
- end;
-
- begin
- if CanEditShow then
- begin
- if FInplaceEdit = nil then
- begin
- FInplaceEdit := CreateEditor;
- FInplaceEdit.SetGrid(Self);
- FInplaceEdit.Parent := Self;
- UpdateEditor;
- end
- else
- begin
- if (Col <> FInplaceCol) or (Row <> FInplaceRow) then
- begin
- try
- HideEdit;
- except
- //<> here we respond to an invalid value
- if Assigned(Columns[Col].FOnInvalidValue) then
- Columns[Col].FOnInvalidValue(self)
- else
- MessageBeep(0);
- end;
- UpdateEditor;
- end;
- end;
- if CanEditShow then FInplaceEdit.Move(CellRect(Col, Row));
- end;
- end;
-
- procedure TQDBGrid.UpdateText;
- begin
- if (FInplaceCol <> -1) and (FInplaceRow <> -1) then
- SetEditText(FInplaceCol, FInplaceRow, FInplaceEdit.Text);
- end;
-
- procedure TQDBGrid.WMChar(var Msg: TWMChar);
- begin
- if (goEditing in Options) and (Char(Msg.CharCode) in [^H, #32..#255]) then
- ShowEditorChar(Char(Msg.CharCode))
- else
- inherited;
- end;
-
- procedure TQDBGrid.WMCommand(var Message: TWMCommand);
- begin
- with Message do
- begin
- if (FInplaceEdit <> nil) and (Ctl = FInplaceEdit.Handle) then
- case NotifyCode of
- EN_CHANGE: UpdateText;
- end;
- end;
- end;
-
- procedure TQDBGrid.WMGetDlgCode(var Msg: TWMGetDlgCode);
- begin
- Msg.Result := DLGC_WANTARROWS;
- if goTabs in Options then Msg.Result := Msg.Result or DLGC_WANTTAB;
- if goEditing in Options then Msg.Result := Msg.Result or DLGC_WANTCHARS;
- end;
-
- procedure TQDBGrid.WMKillFocus(var Msg: TWMKillFocus);
- begin
- inherited;
- InvalidateRect(Selection);
- if (FInplaceEdit <> nil) and (Msg.FocusedWnd <> FInplaceEdit.Handle) then
- begin
- try
- HideEdit;
- except
- //<> another place where we respond to invalid edits
- if Assigned(Columns[Col].FOnInvalidValue) then
- Columns[Col].FOnInvalidvalue(self)
- else
- MessageBeep(0);
- end;
- end;
- end;
-
- procedure TQDBGrid.WMLButtonDown(var Message: TMessage);
- begin
- inherited;
- if FInplaceEdit <> nil then FInplaceEdit.FClickTime := GetMessageTime;
- end;
-
- procedure TQDBGrid.WMNCHitTest(var Msg: TWMNCHitTest);
- begin
- DefaultHandler(Msg);
- FHitTest := SmallPointToPoint(Msg.Pos);
- end;
-
- procedure TQDBGrid.WMSetCursor(var Msg: TWMSetCursor);
- var
- FixedInfo: TGridDrawInfo;
- State: TGridState;
- Index: Longint;
- Pos, Ofs: Integer;
- Cur: HCURSOR;
- begin
- Cur := 0;
- with Msg do
- begin
- if HitTest = HTCLIENT then
- begin
- if FGridState = gsNormal then
- begin
- FHitTest := ScreenToClient(FHitTest);
- CalcFixedInfo(FixedInfo);
- CalcSizingState(FHitTest.X, FHitTest.Y, State, Index, Pos, Ofs,
- FixedInfo);
- end else State := FGridState;
- if State = gsRowSizing then
- Cur := Screen.Cursors[crVSplit]
- else if State = gsColSizing then
- Cur := Screen.Cursors[crHSplit]
- end;
- end;
- if Cur <> 0 then SetCursor(Cur)
- else inherited;
- end;
-
- procedure TQDBGrid.WMSetFocus(var Msg: TWMSetFocus);
- begin
- inherited;
- if (FInplaceEdit = nil) or (Msg.FocusedWnd <> FInplaceEdit.Handle) then
- begin
- InvalidateRect(Selection);
- UpdateEdit;
- end;
- end;
-
- procedure TQDBGrid.WMSize(var Msg: TWMSize);
- begin
- inherited;
- UpdateScrollRange;
- end;
-
- procedure TQDBGrid.WMVScroll(var Msg: TWMVScroll);
- begin
- ModifyScrollBar(SB_VERT, Msg.ScrollCode, Msg.Pos);
- end;
-
- procedure TQDBGrid.WMHScroll(var Msg: TWMHScroll);
- begin
- ModifyScrollBar(SB_HORZ, Msg.ScrollCode, Msg.Pos);
- end;
-
- procedure TQDBGrid.CMCancelMode(var Msg: TMessage);
- begin
- if Assigned(FInplaceEdit) then FInplaceEdit.WndProc(Msg);
- inherited;
- end;
-
- procedure TQDBGrid.CMFontChanged(var Message: TMessage);
- begin
- DefaultRowHeight := abs(Font.Height) + FontHeightMargin;
- //<> I'm not sure why I commented this out!
- // if FInplaceEdit <> nil then FInplaceEdit.Font := Font;
- inherited;
- end;
-
- procedure TQDBGrid.CMCtl3DChanged(var Message: TMessage);
- begin
- inherited;
- RecreateWnd;
- end;
-
- procedure TQDBGrid.CMDesignHitTest(var Msg: TCMDesignHitTest);
- begin
- Msg.Result := Longint(BOOL(Sizing(Msg.Pos.X, Msg.Pos.Y)));
- end;
-
- procedure TQDBGrid.CMWantSpecialKey(var Msg: TCMWantSpecialKey);
- begin
- inherited;
- if (goEditing in Options) and (Char(Msg.CharCode) = #13) then Msg.Result := 1;
- end;
-
- procedure TQDBGrid.TimedScroll(Direction: TGridScrollDirection);
- var
- MaxAnchor, NewAnchor: TGridCoord;
- begin
- NewAnchor := FAnchor;
- MaxAnchor.X := ColCount - 1;
- MaxAnchor.Y := RowCount - 1;
- if (sdLeft in Direction) and (FAnchor.X > FixedCols) then Dec(NewAnchor.X);
- if (sdRight in Direction) and (FAnchor.X < MaxAnchor.X) then Inc(NewAnchor.X);
- if (sdUp in Direction) and (FAnchor.Y > FixedRows) then Dec(NewAnchor.Y);
- if (sdDown in Direction) and (FAnchor.Y < MaxAnchor.Y) then Inc(NewAnchor.Y);
- if (FAnchor.X <> NewAnchor.X) or (FAnchor.Y <> NewAnchor.Y) then
- MoveAnchor(NewAnchor);
- end;
-
- procedure TQDBGrid.WMTimer(var Msg: TWMTimer);
- var
- Point: TPoint;
- DrawInfo: TGridDrawInfo;
- ScrollDirection: TGridScrollDirection;
- CellHit: TGridCoord;
- begin
- if not (FGridState in [gsSelecting, gsColMoving]) then Exit;
- GetCursorPos(Point);
- Point := ScreenToClient(Point);
- CalcDrawInfo(DrawInfo);
- ScrollDirection := [];
- with DrawInfo do
- begin
- CellHit := CalcCoordFromPoint(Point.X, Point.Y, DrawInfo);
- case FGridState of
- gsColMoving:
- MoveAndScroll(Point.X, CellHit.X, DrawInfo, Horz, SB_HORZ);
- gsSelecting:
- begin
- if Point.X < Horz.FixedBoundary then Include(ScrollDirection, sdLeft)
- else if Point.X > Horz.FullVisBoundary then Include(ScrollDirection, sdRight);
- if Point.Y < Vert.FixedBoundary then Include(ScrollDirection, sdUp)
- else if Point.Y > Vert.FullVisBoundary then Include(ScrollDirection, sdDown);
- if ScrollDirection <> [] then TimedScroll(ScrollDirection);
- end;
- end;
- end;
- end;
-
- procedure TQDBGrid.ColWidthsChanged;
- begin
- UpdateScrollRange;
- UpdateEdit;
- end;
-
- procedure TQDBGrid.RowHeightsChanged;
- var
- n: integer;
- MaxRowHeight: integer;
- begin
- if MatchRowHeightToFont then
- begin
- //<> scan the columns for the biggest font ...
- MaxRowHeight := abs(Font.Height);
- for n := 1 to Columns.Count - 1 do
- if abs(Columns[n].Font.Height) > MaxRowHeight then
- MaxRowHeight := abs(Columns[n].Font.Height);
- //<> ... and use it for the row
- FDefaultRowHeight := MaxRowHeight + FontHeightMargin;
- InvalidateGrid;
- end;
- UpdateScrollRange;
- UpdateEdit;
- end;
-
- procedure TQDBGrid.DeleteColumn(ACol: Longint);
- begin
- MoveColumn(ACol, ColCount - 1);
- ColCount := ColCount - 1;
- end;
-
- procedure TQDBGrid.UpdateDesigner;
- var
- ParentForm: TForm;
- begin
- if (csDesigning in ComponentState) and HandleAllocated and
- not (csUpdating in ComponentState) then
- begin
- ParentForm := TForm(GetParentForm(Self));
- if Assigned(ParentForm) and Assigned(ParentForm.Designer) then
- ParentForm.Designer.Modified;
- end;
- end;
-
- procedure TQDBGrid.MouseToCell(X, Y: Integer; var ACol, ARow: Longint);
- var
- Coord: TGridCoord;
- begin
- Coord := MouseCoord(X, Y);
- ACol := Coord.X;
- ARow := Coord.Y;
- end;
-
- function TQDBGrid.GetEditMask(ACol, ARow: Longint): string;
- begin
- Result := '';
- if columns.count > ACol then Result := columns[ACol].EditMask;
- if Assigned(FOnGetEditMask) then FOnGetEditMask(Self, ACol, ARow, Result);
- end;
-
- procedure TQDBGrid.ColumnMoved(FromIndex, ToIndex: Longint);
-
- procedure MoveColData(Index: Integer; ARow: TStringSparseList); far;
- begin
- ARow.Move(FromIndex, ToIndex);
- end;
-
- begin
- TSparseList(FData).ForAll(@MoveColData);
- Invalidate;
- if Assigned(FOnColumnMoved) then FOnColumnMoved(Self, FromIndex, ToIndex);
- end;
-
- procedure TQDBGrid.RowMoved(FromIndex, ToIndex: Longint);
- begin
- TSparseList(FData).Move(FromIndex, ToIndex);
- Invalidate;
- end;
-
- function TQDBGrid.GetEditText(ACol, ARow: Longint): string;
- begin
- Result := Cells[ACol, ARow];
- if Assigned(FOnGetEditText) then FOnGetEditText(Self, ACol, ARow, Result);
- end;
-
- procedure TQDBGrid.SetEditText(ACol, ARow: Longint; const Value: string);
- begin
- DisableEditUpdate;
- try
- if Value <> Cells[ACol, ARow] then Cells[ACol, ARow] := Value;
- finally
- EnableEditUpdate;
- end;
- if Assigned(FOnSetEditText) then FOnSetEditText(Self, ACol, ARow, Value);
- end;
-
- //<> important routine draws cells appropriately
- procedure TQDBGrid.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
-
- procedure DrawCellText;
- const
- BaseAlign = DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX or DT_VCENTER or DT_SINGLELINE;
- AlignFlags: array[TAlignment] of Integer = (DT_LEFT or BaseAlign, DT_RIGHT or BaseAlign, DT_CENTER or BaseAlign);
- var
- S: string;
- TmpColor: TColor;
- Alignment: integer;
- m: TMemoryStream;
- Ext: array[0..3] of char;
- GraphicClass: TGraphicClass;
- NewGraphic: TGraphic;
- r: TRect;
- gr, z: extended;
- begin
- S := Cells[ACol, ARow];
- if (gdFocused in AState) or (gdSelected in AState) then
- begin
- TmpColor := Canvas.font.color;
- Canvas.font := Columns[ACol].font;
- Canvas.font.color := TmpColor;
- end
- else
- begin
- Canvas.font := Columns[ACol].font;
- Canvas.brush.color := Columns[ACol].color;
- end;
- if Columns[ACol].Selected or ((gdSelected in AState) and (not (gdFocused in AState) or ([goDrawFocusSelected] * Options <> []))) then
- begin
- Canvas.Brush.Color := clHighlight;
- Canvas.Font.Color := clHighlightText;
- end;
- if Columns[ACol].Displaymask <> '' then
- try
- case Columns[Acol].FieldType of
- ftreal, ftinteger: s := formatfloat(Columns[ACol].Displaymask, strtofloat(s));
- ftdatetime: s := formatdatetime(Columns[ACol].Displaymask, strtodatetime(s));
- end;
- except
- end;
- Alignment := AlignFlags[Columns[ACol].Alignment];
- if (ARow < FixedRows) or (ACol < FixedCols) then
- begin
- if Columns[ACol].Title <> '' then s := Columns[ACol].Title;
- Alignment := AlignFlags[Columns[ACol].TitleAlignment];
- Canvas.brush.color := FixedColor;
- Canvas.Font := Columns[ACol].TitleFont;
- end;
- if (Columns[ACol].FieldType = ftgraphic) and DisplayThumbnails and (ARow >= FixedRows) then
- begin
- QDBItem.ItemIndex := ARow - FixedRows;
- QDBItem.Fetch;
- m := QDBItem.GetField(Columns[ACol].FieldIndex);
- try
- Ext[3] := #0;
- try
- if 3 = m.Read(Ext[0], 3) then
- begin
- GraphicClass := GetGraphicClass(StrPas(Ext));
- if GraphicClass <> nil then
- begin
- NewGraphic := GraphicClass.Create;
- try
- try
- NewGraphic.LoadFromStream(m);
- except
- NewGraphic.Free;
- raise;
- end;
- r := ARect;
- with r do
- begin
- gr := abs(NewGraphic.Height) / abs(NewGraphic.Width);
- z := (gr * (Right - Left)) / (Bottom - Top);
- if z > 1.0 then
- begin
- Right := Trunc((Bottom - Top) / gr) + Left;
- end
- else
- begin
- Bottom := Trunc((Right - Left) * gr) + Top;
- end;
- end;
- Canvas.FillRect(ARect);
- InflateRect(ARect, -2, -2);
- SetBkMode(Canvas.Handle, TRANSPARENT);
- Canvas.StretchDraw(r, NewGraphic);
- finally
- NewGraphic.Free;
- end;
- end;
- end
- else
- begin
- Canvas.FillRect(ARect);
- end;
- except
- end;
- finally
- m.Position := 0;
- end;
- end
- else
- begin
- Canvas.FillRect(ARect);
- InflateRect(ARect, -2, -2);
- SetBkMode(Canvas.Handle, TRANSPARENT);
- DrawText(Canvas.Handle, pchar(s), -1, ARect, Alignment);
- end;
- end;
-
- begin
- DrawCellText;
- if Assigned(FOnDrawCell) then FOnDrawCell(Self, ACol, ARow, ARect, AState);
- end;
-
- procedure TQDBGrid.DisableEditUpdate;
- begin
- Inc(FEditUpdate);
- end;
-
- procedure TQDBGrid.EnableEditUpdate;
- begin
- Dec(FEditUpdate);
- end;
-
- procedure TQDBGrid.Initialize;
- var
- quantum: TSPAQuantum;
- begin
- FTopLeft.X := FixedCols;
- FTopLeft.Y := FixedRows;
- FCurrent := FTopLeft;
- FAnchor := FCurrent;
- if RowCount > 256 then quantum := SPALarge else quantum := SPASmall;
- if FData = nil then FData := TSparseList.Create(quantum);
- end;
-
- procedure TQDBGrid.SetUpdateState(Updating: Boolean);
- begin
- FUpdating := Updating;
- if not Updating and FNeedsUpdating then
- begin
- InvalidateGrid;
- FNeedsUpdating := False;
- end;
- end;
-
- procedure TQDBGrid.UpdateCell(ACol, ARow: Integer);
- begin
- if not FUpdating then InvalidateCell(ACol, ARow)
- else FNeedsUpdating := True;
- if (ACol = Col) and (ARow = Row) and (FEditUpdate = 0) then InvalidateEditor;
- end;
-
- function TQDBGrid.EnsureDataRow(ARow: Integer): Pointer;
- var
- quantum: TSPAQuantum;
- begin
- Result := TStringSparseList(TSparseList(FData)[ARow]);
- if Result = nil then
- begin
- if ColCount > 512 then quantum := SPALarge else quantum := SPASmall;
- Result := TStringSparseList.Create(quantum);
- TSparseList(FData)[ARow] := Result;
- end;
- end;
-
- function TQDBGrid.GetCells(ACol, ARow: Integer): string;
- var
- ssl: TStringSparseList;
- begin
- ssl := TStringSparseList(TSparseList(FData)[ARow]);
- if ssl = nil then Result := '' else Result := ssl[ACol];
- end;
-
- procedure TQDBGrid.SetCells(ACol, ARow: Integer; const Value: string);
- begin
- TQDBGridStrings(EnsureDataRow(ARow))[ACol] := Value;
- UpdateCell(ACol, ARow);
- end;
-
- function TQDBGrid.CreateColumns: TQDBGridColumns;
- begin
- Result := TQDBGridColumns.Create(Self, TColumn);
- end;
-
- procedure TQDBGrid.SetColumns(Value: TQDBGridColumns);
- begin
- Columns.Assign(Value);
- end;
-
- //<> fetch structure from file
- procedure TQDBGrid.LoadFieldStructure;
- var
- n: integer;
- ok: boolean;
- begin
- ok := true;
- //<> first check to see if all titles already match file
- //<> this way we can have columns in a different order to file
- for n := FixedCols to Columns.Count - 1 do
- begin
- //<> if any Title fails to match a field ok is set false
- if FQDBItem.FieldIndex(Columns[n].Title) = -1 then
- begin
- ok := false;
- break;
- end
- else
- begin
- Columns[n].FFieldIndex := n - FixedCols;
- Columns[n].FFieldType := QDBItem.FieldTypes[n - FixedCols];
- end;
- end;
- if not ok then
- begin
- //<> load all the field names from the file
- setcolumncount(FixedCols + FQDBItem.FieldCount);
- for n := FixedCols to Columns.Count - 1 do
- begin
- Columns[n].Title := QDBItem.FieldNames[n - FixedCols];
- Columns[n].FFieldIndex := n - FixedCols;
- Columns[n].FFieldType := QDBItem.FieldTypes[n - FixedCols];
- end;
- end;
- end;
-
- //<>
- procedure TQDBGrid.LoadRow(ARow: longint);
- var
- n: longint;
- begin
- if Assigned(FQDBItem) then
- begin
- QDBItem.ItemIndex := ARow - FixedRows;
- QDBItem.Fetch;
- for n := FixedCols to Columns.Count - 1 do
- begin
- //<> booleans are translated to the Marlett glyphs
- //<> and graphics left blank
- case Columns[n].FieldType of
- ftboolean: if QDBItem.AsBoolean[FQDBItem.FieldIndex(Columns[n].Title)] then
- Cells[n, ARow] := chr(97)
- else
- Cells[n, ARow] := chr(114);
- ftgraphic: Cells[n, ARow] := '';
- else
- // other types are loaded as text
- Cells[n, ARow] := QDBItem.AsString[FQDBItem.FieldIndex(Columns[n].Title)];
- end;
- end;
- end;
- end;
-
- procedure TQDBGrid.SaveCell(ACol, ARow: longint);
- begin
- if Assigned(FQDBItem) and not QDBItem.ReadOnly then
- begin
- QDBItem.ItemIndex := ARow - FixedRows;
- QDBItem.Fetch;
- //<> booleans have to be translated but others are stored as text
- //<> AsString is clever enough ignore graphics etc.
- case Columns[ACol].FieldType of
- ftboolean :
- begin
- QDBItem.AsBoolean[QDBItem.FieldIndex(Columns[ACol].Title)] := (Cells[ACol, ARow] = chr(97));
- end;
- else
- QDBItem.AsString[FQDBItem.FieldIndex(Columns[ACol].Title)] := Cells[ACol, ARow];
- end;
- QDBItem.Store;
- end;
- end;
-
- procedure TQDBGrid.SaveRow(ARow: longint);
- var
- n: longint;
- begin
- HideEditor;
- FQDBItem.ItemIndex := ARow - FixedRows;
- FQDBItem.Fetch;
- for n := FixedCols to Columns.Count - 1 do
- begin
- case Columns[n].FieldType of
- ftboolean :
- begin
- QDBItem.AsBoolean[QDBItem.FieldIndex(Columns[n].Title)] := (Cells[n, ARow] = chr(97));
- end;
- else
- QDBItem.AsString[FQDBItem.FieldIndex(Columns[n].Title)] := Cells[n, ARow];
- end;
- end;
- FQDBItem.Store
- end;
-
- procedure TQDBGrid.Load;
- var
- n: longint;
- begin
- if Assigned(FQDBItem) and FQDBItem.Ready then
- begin
- //<> set as many rows as there items in the file
- RowCount := FixedRows + FQDBItem.Count;
- //<> load the visible ones
- for n := FixedRows to VisibleRowCount do
- LoadRow(n);
- end;
- Invalidate;
- end;
-
- procedure TQDBGrid.ReLoad;
- var
- n: longint;
- m: longint;
- begin
- if Assigned(FQDBItem) and FQDBItem.Ready then
- begin
- //<> calculate which rows we need to reload
- m := TopRow + VisibleRowCount - FixedRows;
- if m >= FQDBItem.Count + FixedRows then m := FQDBItem.Count - 1 + FixedRows;
- for n := TopRow to m do
- LoadRow(n);
- end;
- Invalidate;
- end;
-
- //<> when the grid is initially loaded we need to synchronize columns and cols
- procedure TQDBGrid.Loaded;
- begin
- inherited Loaded;
- if Columns.Count > 1 then
- ColCount := Columns.Count;
- end;
-
-
- end.
-
-