ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/DBControlGrid.pas
Revision: 315
Committed: Thu Feb 25 11:56:36 2021 UTC (3 years, 8 months ago) by tony
Content type: text/x-pascal
File size: 46748 byte(s)
Log Message:
Updated for IBX 4 release

File Contents

# User Rev Content
1 tony 143
2 tony 23 {
3     /***************************************************************************
4     DBControlGrid.pas
5     -----------
6     An interface to DB aware Controls
7     Initial Revision : Sun Mar 8 2015
8    
9    
10     ***************************************************************************/
11    
12     Unashameably hacked from DBGrid.Pas (Copyright (C) 2003 Jesus Reyes Aguilar.)
13     by Tony Whyman (tony@mwasoftware.co.uk) .Additional source code is
14     Copyright (c) McCallum Whyman Associates Ltd (trading as MWA Software) 2015.
15    
16     This unit defines TDBControlGrid: a lookalike rather than a clone for the Delphi
17     TDBCrtlGrid. TDBControlGrid is a single column grid that replicates a TWinControl
18     - typically a TPanel or a TFrame in each row. Each row corresponding to the rows
19     of the linked DataSource. Any data aware control on the replicated (e.g.) TPanel
20     will then appear to have the appropriate value for the row.
21    
22     The replicated control is not part of this control but must be added by the
23     programmer at design time, and linked to the "DrawPanel" property.
24    
25     Rows can be edited, inserted (append only) or deleted.
26    
27     Distributed and licensed under the Library GNU General Public License
28     see https://www.gnu.org/licenses/lgpl.html with the following modification:
29    
30     As a special exception, the copyright holders of this library give you
31     permission to link this library with independent modules to produce an
32     executable, regardless of the license terms of these independent modules,
33     and to copy and distribute the resulting executable under terms of your choice,
34     provided that you also meet, for each linked independent module, the terms
35     and conditions of the license of that module. An independent module is a
36     module which is not derived from or based on this library. If you modify this
37     library, you may extend this exception to your version of the library, but
38     you are not obligated to do so. If you do not wish to do so, delete this
39     exception statement from your version.
40    
41     }
42     unit DBControlGrid;
43    
44     {$mode objfpc}{$H+}
45    
46     interface
47    
48     uses
49     Classes, Controls, SysUtils, DB, Grids, DBGrids, Graphics, StdCtrls,
50     LMessages;
51    
52     {
53     The TRowCache is where we keep track of the DataSet and cache images of each row.
54     TDBControlGrid is really a slight of hand. Only the active record is shown in
55     the panel and the others are cached and displayed as images.
56    
57     The image cache is indexed by TDataSet.RecNo and accessed by current active
58     record number (the data being displayed on the panel) and row offset from this
59     record number.
60    
61     This is efficient but gives us a problem as the TDataSet model does not remove
62     deleted records. Instead it simply marks them as deleted. Likewise, we need to
63     keep track of deleted rows and skip over them when accessing the cache.
64    
65     When alternate row colours are in use, the cache is also used to keep track of the
66     correct row colour as we must similarly ignore delete rows when calculating the
67     correct colour. Odd and Even row numbers is not good enough here.
68     }
69    
70     type
71     { TRowCache }
72    
73     TRowCache = class
74     private
75 tony 263 type
76     TRowCacheState = (rcEmpty,rcPresent,rcDeleted);
77     TRowDetails = record
78     FState: TRowCacheState;
79     FAlternateColor: boolean;
80     FBitmap: TBitmap;
81     end;
82    
83     private
84 tony 23 FAltColorStartNormal: boolean;
85     FHeight: integer;
86     FList: array of TRowDetails;
87     FUseAlternateColors: boolean;
88     FWidth: integer;
89     procedure FreeImages(Reset: boolean);
90     function GetAlternateColor(RecNo: integer): boolean;
91     function Render(Control: TWinControl): TBitmap;
92     procedure ExtendCache(aMaxIndex: integer);
93     procedure OnWidthChange(Sender: TObject);
94     procedure SetHeight(AValue: integer);
95     procedure SetUseAlternateColors(AValue: boolean);
96     procedure SetWidth(AValue: integer);
97     public
98     constructor Create;
99     destructor Destroy; override;
100     procedure ClearCache;
101     function Add2Cache(RecNo: Longint; Control: TWinControl): TBitmap;
102     function GetRowImage(RecNo, Offset: integer): TBitmap;
103 tony 29 procedure InvalidateRowImage(RecNo: integer);
104 tony 23 function IsEmpty(RecNo: integer): boolean;
105     procedure MarkAsDeleted(RecNo: integer);
106     property AlternateColor[RecNo: integer]: boolean read GetAlternateColor;
107     property Width: integer read FWidth write SetWidth;
108     property Height: integer read FHeight write SetHeight;
109     property AltColorStartNormal: boolean read FAltColorStartNormal write FAltColorStartNormal;
110     property UseAlternateColors: boolean read FUseAlternateColors write SetUseAlternateColors;
111     end;
112    
113     { TDBControlGridDataLink }
114    
115     TDBControlGridDataLink = class(TComponentDataLink)
116     private
117     FOnCheckBrowseMode: TDataSetNotifyEvent;
118     protected
119     procedure CheckBrowseMode; override;
120     public
121     property OnCheckBrowseMode: TDataSetNotifyEvent read FOnCheckBrowseMode write FOnCheckBrowseMode;
122     end;
123    
124     TKeyDownHandler = procedure (Sender: TObject; var Key: Word; Shift: TShiftState; var Done: boolean) of object;
125    
126     TPanelGridOption = (dgpIndicator,dgpDisableInsert,dgpCancelOnExit);
127     TPanelGridOptions = set of TPanelGridOption;
128    
129     { TDBControlGrid }
130    
131     TDBControlGrid = class(TCustomGrid)
132     private
133     { Private declarations }
134     FDataLink: TDBControlGridDataLink;
135     FDefaultPositionAtEnd: boolean;
136     FDrawPanel: TWinControl;
137     FDrawingActiveRecord: boolean;
138     FOldPosition: Integer;
139     FOnKeyDownHander: TKeyDownHandler;
140     FOptions: TPanelGridOptions;
141     FWeHaveFocus: boolean;
142     FRowCache: TRowCache;
143     FDrawRow: integer; {The current row in the draw panel}
144     FSelectedRow: integer; {The row containing the current selection}
145     FSelectedRecNo: integer; {The DataSet RecNo for the current row}
146     FRequiredRecNo: integer; {Used after a big jump and is the dataset recno
147     that we want to end up with}
148     FInCacheRefresh: boolean; {Cache refresh in progress during paint}
149     FCacheRefreshQueued: boolean; {cache refresh requested during wmpaint}
150     FModified: boolean;
151     FLastRecordCount: integer;
152    
153     {Used to pass mouse clicks to panel when focused row changes}
154     FLastMouse: TPoint;
155     FLastMouseButton: TMouseButton;
156     FLastMouseShiftState: TShiftState;
157    
158 tony 31 function ActiveControl: TControl;
159 tony 23 procedure EmptyGrid;
160     function GetDataSource: TDataSource;
161     function GetRecordCount: Integer;
162     procedure GetScrollbarParams(out aRange, aPage, aPos: Integer);
163     function GridCanModify: boolean;
164     procedure DoDrawRow(aRow: integer; aRect: TRect; aState: TGridDrawState);
165     procedure DoMoveRecord(Data: PtrInt);
166 tony 27 procedure DoSelectNext(Data: PtrInt);
167 tony 23 procedure DoScrollDataSet(Data: PtrInt);
168     procedure DoSetupDrawPanel(Data: PtrInt);
169     procedure DoSendMouseClicks(Data: PtrInt);
170     procedure KeyDownHandler(Sender: TObject; var Key: Word; Shift: TShiftState);
171     procedure OnRecordChanged(Field:TField);
172     procedure OnCheckBrowseMode(aDataSet: TDataSet);
173     procedure OnDataSetChanged(aDataSet: TDataSet);
174     procedure OnDataSetOpen(aDataSet: TDataSet);
175     procedure OnDataSetClose(aDataSet: TDataSet);
176     procedure OnDrawPanelResize(Sender: TObject);
177     procedure OnEditingChanged(aDataSet: TDataSet);
178     procedure OnInvalidDataSet(aDataSet: TDataSet);
179     procedure OnInvalidDataSource(aDataSet: TDataset);
180     procedure OnLayoutChanged(aDataSet: TDataSet);
181     procedure OnNewDataSet(aDataSet: TDataset);
182     procedure OnDataSetScrolled(aDataSet:TDataSet; Distance: Integer);
183     procedure OnUpdateData(aDataSet: TDataSet);
184     procedure SetDataSource(AValue: TDataSource);
185     procedure SetDrawPanel(AValue: TWinControl);
186     procedure SetOptions(AValue: TPanelGridOptions);
187     procedure SetupDrawPanel(aRow: integer);
188     function UpdateGridCounts: Integer;
189     procedure UpdateBufferCount;
190     procedure UpdateDrawPanelBounds(aRow: integer);
191     procedure UpdateScrollbarRange;
192     procedure WMVScroll(var Message : TLMVScroll); message LM_VScroll;
193     function ISEOF: boolean;
194     function ValidDataSet: boolean;
195     function InsertCancelable: boolean;
196     protected
197     { Protected declarations }
198     function GetBufferCount: integer; virtual;
199     procedure DoEnter; override;
200     procedure DoExit; override;
201     procedure DoGridResize;
202     procedure DoOnResize; override;
203     procedure DrawAllRows; override;
204     procedure DrawRow(ARow: Integer); override;
205     procedure DrawCell(aCol,aRow: Integer; aRect: TRect; aState:TGridDrawState); override;
206     procedure DrawIndicator(ACanvas: TCanvas; aRow: integer; R: TRect; Opt: TDataSetState; MultiSel: boolean); virtual;
207     procedure GridMouseWheel(shift: TShiftState; Delta: Integer); override;
208     procedure KeyDown(var Key : Word; Shift : TShiftState); override;
209     procedure LinkActive(Value: Boolean); virtual;
210     procedure LayoutChanged; virtual;
211     procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
212     procedure MouseUp(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
213     procedure MoveSelection; override;
214     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
215     procedure PrepareCanvas(aCol,aRow: Integer; aState:TGridDrawState); override;
216     procedure ResetSizes; override;
217     procedure SetColor(Value: TColor); override;
218     procedure UpdateActive; virtual;
219     procedure UpdateData; virtual;
220     procedure UpdateShowing; override;
221     procedure UpdateVertScrollbar(const aVisible: boolean; const aRange,aPage,aPos: Integer); override;
222     public
223     { Public declarations }
224     constructor Create(AOwner: TComponent); override;
225     destructor Destroy; override;
226     function MouseToRecordOffset(const x, y: Integer; out RecordOffset: Integer
227     ): TGridZone;
228     function ExecuteAction(AAction: TBasicAction): Boolean; override;
229     function UpdateAction(AAction: TBasicAction): Boolean; override;
230     property Datalink: TDBControlGridDataLink read FDatalink;
231     published
232     { Published declarations }
233     property Align;
234     property AlternateColor;
235     property AltColorStartNormal;
236     property Anchors;
237     property BiDiMode;
238     property BorderSpacing;
239     property BorderStyle;
240     property CellHintPriority;
241     property Color;
242     property Constraints;
243     property DataSource: TDataSource read GetDataSource write SetDataSource;
244     property DefaultPositionAtEnd: boolean read FDefaultPositionAtEnd write FDefaultPositionAtEnd;
245     property DragCursor;
246     property DragMode;
247     property DrawPanel: TWinControl read FDrawPanel write SetDrawPanel;
248     property Enabled;
249     property FixedColor;
250     property FixedCols;
251     property Flat;
252     property Font;
253     property Options: TPanelGridOptions read FOptions write SetOptions;
254     property ParentBiDiMode;
255     property ParentColor default false;
256     property ParentFont;
257     property PopupMenu;
258     property Scrollbars default ssAutoVertical;
259     property ShowHint;
260     property TabOrder;
261     property TabStop;
262     property UseXORFeatures;
263     property Visible;
264     property OnContextPopup;
265     property OnDblClick;
266     property OnDragDrop;
267     property OnDragOver;
268     property OnEndDrag;
269     property OnEnter;
270     property OnExit;
271     property OnGetCellHint;
272     property OnKeyDown;
273     property OnKeyPress;
274     property OnKeyUp;
275     property OnKeyDownHander: TKeyDownHandler read FOnKeyDownHander write FOnKeyDownHander;
276     property OnMouseDown;
277     property OnMouseEnter;
278     property OnMouseLeave;
279     property OnMouseMove;
280     property OnMouseUp;
281     property OnMouseWheel;
282     property OnMouseWheelDown;
283     property OnMouseWheelUp;
284     property OnPrepareCanvas;
285     property OnStartDrag;
286     property OnUTF8KeyPress;
287     end;
288    
289     implementation
290    
291 tony 45 uses LCLType, Math, LCLIntf, Forms, LCLMessageGlue, EditBtn, MaskEdit;
292 tony 23
293     { TDBControlGridDataLink }
294    
295     procedure TDBControlGridDataLink.CheckBrowseMode;
296     begin
297     inherited CheckBrowseMode;
298     if assigned(FOnCheckBrowseMode) then
299     OnCheckBrowseMode(DataSet);
300     end;
301    
302     { TRowCache }
303    
304     function TRowCache.Render(Control: TWinControl): TBitmap;
305     var Container: TBitmap;
306     begin
307     Container := TBitmap.Create;
308     try
309     Container.SetSize(Control.Width,Control.Height);
310 tony 315 Container.Canvas.Brush.Color := control.Color;
311 tony 23 Control.PaintTo(Container.Canvas,0,0);
312     except
313     Container.Free;
314     raise
315     end;
316     Result := Container;
317     end;
318    
319     procedure TRowCache.FreeImages(Reset: boolean);
320     var i: integer;
321     altColor: boolean;
322     begin
323     altColor := not AltColorStartNormal;
324     for i := 0 to Length(FList) - 1 do
325     begin
326     if (FList[i].FState <> rcEmpty) and (FList[i].FBitmap <> nil) then
327     begin
328     FList[i].FBitmap.Free;
329     FList[i].FBitmap := nil;
330     end;
331     if Reset or (FList[i].FState = rcPresent) then
332     FList[i].FState := rcEmpty;
333     if FList[i].FState <> rcDeleted then
334     begin
335     FList[i].FAlternateColor := altColor;
336     altColor := not altColor;
337     end;
338     end;
339     end;
340    
341     function TRowCache.GetAlternateColor(RecNo: integer): boolean;
342     begin
343     ExtendCache(RecNo);
344     Dec(RecNo);
345     if (RecNo >= 0) and (RecNo < Length(FList)) then
346     Result := FList[RecNo].FAlternateColor
347     else
348     Result := false;
349     end;
350    
351     procedure TRowCache.ExtendCache(aMaxIndex: integer);
352     var i: integer;
353     StartIndex: integer;
354     altColor: boolean;
355     begin
356     if aMaxIndex > Length(FList) then
357     begin
358     aMaxIndex := aMaxIndex + 10;
359     StartIndex := Length(FList);
360     SetLength(FList,aMaxIndex);
361     if not UseAlternateColors then
362     altColor := false
363     else
364     if StartIndex = 0 then
365     altColor := not AltColorStartNormal
366     else
367     altColor := not FList[StartIndex-1].FAlternateColor;
368    
369     for i := StartIndex to Length(FList) - 1 do
370     begin
371     FList[i].FState := rcEmpty;
372 tony 45 FList[i].FBitmap := nil;
373 tony 23 FList[i].FAlternateColor := altColor;
374     if UseAlternateColors then
375     altColor := not altColor;
376     end;
377     end;
378     end;
379    
380     procedure TRowCache.OnWidthChange(Sender: TObject);
381     begin
382     FreeImages(false);
383     end;
384    
385     procedure TRowCache.SetHeight(AValue: integer);
386     begin
387     if FHeight = AValue then Exit;
388     FHeight := AValue;
389     FreeImages(false);
390     end;
391    
392     procedure TRowCache.SetUseAlternateColors(AValue: boolean);
393     begin
394     if FUseAlternateColors = AValue then Exit;
395     FUseAlternateColors := AValue;
396     FreeImages(false);
397     end;
398    
399     procedure TRowCache.SetWidth(AValue: integer);
400     begin
401     if FWidth = AValue then Exit;
402     FWidth := AValue;
403     FreeImages(false);
404     end;
405    
406     constructor TRowCache.Create;
407     begin
408     SetLength(FList,0);
409     end;
410    
411     destructor TRowCache.Destroy;
412     begin
413     ClearCache;
414     inherited Destroy;
415     end;
416    
417     procedure TRowCache.ClearCache;
418     begin
419     FreeImages(true);
420 tony 35 SetLength(FList,0);
421 tony 23 end;
422    
423     function TRowCache.Add2Cache(RecNo: Longint; Control: TWinControl): TBitmap;
424     begin
425     Dec(RecNo); {Adust to zero base}
426     ExtendCache(RecNo + 1);
427     FList[RecNo].FState := rcPresent;
428 tony 45 if FList[RecNo].FBitmap <> nil then
429     FList[RecNo].FBitmap.Free;
430 tony 23 FList[RecNo].FBitmap := Render(Control);
431     Result := FList[RecNo].FBitmap;
432     end;
433    
434     function TRowCache.GetRowImage(RecNo, Offset: integer): TBitmap;
435     begin
436     Result := nil;
437     Dec(RecNo); {adjust to zero base}
438     if (RecNo < 0) or (RecNo >= Length(FList)) then
439     Exit;
440    
441     if Offset >= 0 then
442     repeat
443     while (RecNo < Length(FList)) and (FList[RecNo].FState = rcDeleted) do
444     Inc(RecNo);
445    
446     if RecNo >= Length(FList) then
447     Exit;
448    
449     if Offset = 0 then
450     begin
451     if FList[RecNo].FState = rcPresent then
452     Result := FList[RecNo].FBitmap;
453     Exit;
454     end;
455     Inc(RecNo);
456     Dec(Offset);
457     until false
458     else
459     repeat
460     Inc(Offset);
461     Dec(RecNo);
462     while (RecNo > 0) and (FList[RecNo].FState = rcDeleted) do
463     Dec(RecNo);
464    
465     if RecNo < 0 then
466     Exit;
467    
468     if Offset = 0 then
469     begin
470     if FList[RecNo].FState = rcPresent then
471     Result := FList[RecNo].FBitmap;
472     Exit;
473     end;
474     until false;
475     end;
476    
477 tony 29 procedure TRowCache.InvalidateRowImage(RecNo: integer);
478     begin
479     Dec(RecNo); {adjust to zero base}
480     if (RecNo < 0) or (RecNo >= Length(FList)) then
481     Exit;
482    
483     if FList[RecNo].FState = rcPresent then
484     begin
485     FList[RecNo].FBitmap.Free;
486 tony 45 FList[RecNo].FBitmap := nil;
487 tony 29 FList[RecNo].FState := rcEmpty;
488     end;
489     end;
490    
491 tony 23 function TRowCache.IsEmpty(RecNo: integer): boolean;
492     begin
493     Dec(RecNo);
494     Result := (RecNo < 0) or (RecNo >= Length(FList)) or (FList[RecNo].FState = rcEmpty);
495     end;
496    
497     procedure TRowCache.MarkAsDeleted(RecNo: integer);
498     var altColor: boolean;
499     i: integer;
500     begin
501     Dec(RecNo); {adjust to zero base}
502     if (RecNo < 0) or (RecNo >= Length(FList)) then
503     Exit;
504    
505     FList[RecNo].FState := rcDeleted;
506     if not UseAlternateColors then
507     Exit;
508    
509     {Reset Alternate Colours}
510    
511     if RecNo = 0 then
512     altColor := not AltColorStartNormal
513     else
514     altColor := not FList[RecNo-1].FAlternateColor;
515    
516     for i := RecNo + 1 to Length(FList) - 1 do
517     begin
518     if FList[i].FState <> rcDeleted then
519     begin
520     FList[i].FAlternateColor := altColor;
521     altColor := not altColor;
522     if FList[i].FState = rcPresent then
523     begin
524     FList[i].FBitmap.Free;
525     FList[i].FState := rcEmpty;
526     end;
527     end;
528     end;
529     end;
530    
531     { TDBControlGrid }
532    
533 tony 31 function TDBControlGrid.ActiveControl: TControl;
534     var AParent: TWinControl;
535     begin
536     Result := nil;
537     AParent := Parent;
538     while (AParent <> nil) and not (AParent is TCustomForm) do
539     AParent := AParent.Parent;
540     if (AParent <> nil) and (AParent is TCustomForm)then
541     Result := TCustomForm(AParent).ActiveControl;
542     end;
543    
544 tony 23 procedure TDBControlGrid.EmptyGrid;
545     var
546     OldFixedRows: Integer;
547     begin
548     OldFixedRows := FixedRows;
549     Clear;
550 tony 35 FRowCache.ClearCache;
551 tony 23 RowCount := OldFixedRows + 1;
552     if dgpIndicator in FOptions then
553     ColWidths[0]:=12;
554     if assigned(FDrawPanel) then
555     FDrawPanel.Visible := false;
556     end;
557    
558     function TDBControlGrid.GetDataSource: TDataSource;
559     begin
560     Result:= FDataLink.DataSource;
561     end;
562    
563     function TDBControlGrid.GetRecordCount: Integer;
564     begin
565     if assigned(FDataLink.DataSet) then
566     result := FDataLink.DataSet.RecordCount
567     else
568     result := 0;
569     end;
570    
571     procedure TDBControlGrid.GetScrollbarParams(out aRange, aPage, aPos: Integer);
572     begin
573     if (FDatalink<>nil) and (FDataLink.DataSet <> nil) and FDatalink.Active then begin
574     if FDatalink.dataset.IsSequenced then begin
575     aRange := GetRecordCount + VisibleRowCount - 1;
576     aPage := VisibleRowCount;
577     if aPage<1 then aPage := 1;
578     if FDatalink.BOF then aPos := 0 else
579     if FDatalink.EOF then aPos := aRange
580     else
581     aPos := FDataLink.DataSet.RecNo - 1; // RecNo is 1 based
582     if aPos<0 then aPos:=0;
583     end else begin
584     aRange := 6;
585     aPage := 2;
586     if FDatalink.EOF then aPos := 4 else
587     if FDatalink.BOF then aPos := 0
588     else aPos := 2;
589     end;
590     end else begin
591     aRange := 0;
592     aPage := 0;
593     aPos := 0;
594     end;
595     end;
596    
597     function TDBControlGrid.GridCanModify: boolean;
598     begin
599     result := not FDataLink.ReadOnly
600     and ValidDataSet and FDatalink.DataSet.CanModify;
601     end;
602    
603     procedure TDBControlGrid.DoDrawRow(aRow: integer; aRect: TRect;
604     aState: TGridDrawState);
605     var CachedRow: TBitmap;
606     begin
607     CachedRow := FRowCache.GetRowImage(FSelectedRecNo,aRow-FDrawRow);
608     {if the row is in the cache then draw it - otherwise schedule a cache refresh cycle}
609     if CachedRow = nil then
610     begin
611     if not FCacheRefreshQueued then
612     begin
613     FCacheRefreshQueued := true;
614     Application.QueueAsyncCall(@DoMoveRecord,PtrInt(aRow));
615 tony 143 end;
616     Canvas.FillRect(aRect);
617 tony 23 end
618     else
619     Canvas.Draw(aRect.Left,aRect.Top,CachedRow)
620     end;
621    
622     procedure TDBControlGrid.DoMoveRecord(Data: PtrInt);
623     var aRow: integer;
624     begin
625     if AppDestroying in Application.Flags then Exit;
626    
627     FCacheRefreshQueued := false;
628     aRow := integer(Data);
629     FInCacheRefresh := true;
630     if assigned(FDataLink.DataSet) then
631 tony 35 FDatalink.DataSet.MoveBy(aRow - FDrawRow);
632 tony 23 end;
633    
634     procedure TDBControlGrid.DoSetupDrawPanel(Data: PtrInt);
635     begin
636     if AppDestroying in Application.Flags then Exit;
637     SetupDrawPanel(FDrawRow);
638     end;
639    
640     procedure TDBControlGrid.DoSendMouseClicks(Data: PtrInt);
641     var P: TPoint;
642     Control: TControl;
643     begin
644     if AppDestroying in Application.Flags then Exit;
645    
646     if assigned(FDrawPanel) and (FLastMouse.X <> 0) then
647     begin
648     P := ClientToScreen(FLastMouse);
649     Control := FindControlAtPosition(P,false);
650     if (Control <> nil) and (Control is TWinControl) then
651     TWinControl(Control).SetFocus
652     else
653     Control := FDrawPanel;
654    
655     P := Control.ScreenToClient(P);
656    
657     LCLSendMouseDownMsg(Control,P.X,P.Y,FLastMouseButton,FLastMouseShiftState);
658     LCLSendMouseUpMsg(Control,P.X,P.Y, FLastMouseButton,FLastMouseShiftState);
659    
660     end;
661     FLastMouse.X := 0;
662     end;
663    
664     procedure TDBControlGrid.KeyDownHandler(Sender: TObject; var Key: Word;
665     Shift: TShiftState);
666     var Done: boolean;
667 tony 31 AControl: TControl;
668 tony 23 begin
669 tony 315 if Visible and assigned(FDrawPanel) and FDrawPanel.Visible and FWeHaveFocus
670     and (Self.Owner=Screen.ActiveForm) then
671 tony 23 begin
672 tony 31 AControl := ActiveControl;
673     if (AControl <> nil) and (AControl is TCustomComboBox)
674     and ((Key in [VK_UP,VK_DOWN]) or
675     (TCustomComboBox(AControl).DroppedDown and (Key = VK_RETURN)) or
676     ((TCustomComboBox(AControl).Text <> '') and (Key = VK_ESCAPE))) then
677     Exit; {ignore these keys if we are in a combobox}
678    
679     if (AControl <> nil) and (AControl is TCustomMemo)
680 tony 35 and (Key in [VK_RETURN,VK_UP,VK_DOWN]) then Exit; {Ignore Return in a CustomMemo}
681 tony 31
682 tony 45 if (AControl <> nil) and (AControl is TCustomGrid)
683     and (Key in [VK_RETURN,VK_UP,VK_DOWN,VK_TAB]) then Exit; {Ignore Return in a CustomMemo}
684    
685     if (AControl <> nil) and ((AControl is TDateEdit) or (AControl is TCustomMaskedit))
686     and (Key in [VK_RETURN,VK_UP,VK_DOWN,
687     VK_ESCAPE,VK_LEFT,VK_RIGHT]) then Exit; {Ignore Return in a CustomMemo}
688 tony 23 Done := false;
689     if assigned(FOnKeyDownHander) then
690     OnKeyDownHander(Sender,Key,Shift,Done);
691     if Done then Exit;
692    
693     KeyDown(Key,Shift)
694     end;
695     end;
696    
697     procedure TDBControlGrid.OnRecordChanged(Field: TField);
698     begin
699     UpdateActive
700     end;
701    
702     procedure TDBControlGrid.OnCheckBrowseMode(aDataSet: TDataSet);
703     var RecNo: integer;
704     begin
705     if assigned(FDrawPanel) and (aDataSet.RecNo > 0)
706     and (FModified or (FRowCache.IsEmpty(aDataSet.RecNo))) then
707     begin
708     RecNo := aDataSet.RecNo;
709     Application.ProcessMessages;
710     if RecNo = aDataSet.RecNo then {Guard against sudden changes}
711 tony 45 FRowCache.Add2Cache(RecNo,FDrawPanel);
712 tony 23 end;
713     end;
714    
715     procedure TDBControlGrid.OnDataSetChanged(aDataSet: TDataSet);
716     begin
717 tony 35 if aDataSet.State = dsBrowse then
718 tony 23 begin
719 tony 35 if GetRecordCount = 0 then
720     begin
721     {Must be closed/reopened}
722     FRowCache.ClearCache;
723     FSelectedRow := 0;
724     end
725     else
726     if FLastRecordCount > GetRecordCount then
727     begin
728     {must be delete}
729     FRowCache.MarkAsDeleted(FSelectedRecNo);
730     Dec(FSelectedRow);
731     end;
732 tony 23 LayoutChanged;
733     end;
734     FLastRecordCount := GetRecordCount;
735     if aDataSet.State = dsInsert then
736 tony 27 begin
737 tony 23 FRequiredRecNo := aDataSet.RecNo + 1;
738 tony 27 Application.QueueAsyncCall(@DoSelectNext,0);
739     end;
740 tony 23 UpdateActive
741     end;
742    
743     procedure TDBControlGrid.OnDataSetOpen(aDataSet: TDataSet);
744     begin
745     LinkActive(true);
746     UpdateActive;
747     end;
748    
749     procedure TDBControlGrid.OnDataSetClose(aDataSet: TDataSet);
750     begin
751     LinkActive(false);
752     end;
753    
754     procedure TDBControlGrid.OnDrawPanelResize(Sender: TObject);
755     begin
756     FRowCache.Height := FDrawPanel.Height;
757     DefaultRowHeight := FDrawPanel.Height;
758     end;
759    
760     procedure TDBControlGrid.OnEditingChanged(aDataSet: TDataSet);
761     begin
762     FModified := true;
763     end;
764    
765     procedure TDBControlGrid.OnInvalidDataSet(aDataSet: TDataSet);
766     begin
767     LinkActive(False);
768     end;
769    
770     procedure TDBControlGrid.OnInvalidDataSource(aDataSet: TDataset);
771     begin
772     LinkActive(False);
773     end;
774    
775     procedure TDBControlGrid.OnLayoutChanged(aDataSet: TDataSet);
776     begin
777     LayoutChanged;
778     end;
779    
780     procedure TDBControlGrid.OnNewDataSet(aDataSet: TDataset);
781     begin
782     LinkActive(True);
783     UpdateActive;
784     end;
785    
786     procedure TDBControlGrid.OnDataSetScrolled(aDataSet: TDataSet; Distance: Integer);
787     begin
788     UpdateScrollBarRange;
789     if Distance <> 0 then
790     begin
791     FDrawRow := FixedRows + FDataLink.ActiveRecord;
792    
793     if not FInCacheRefresh then
794     begin
795     Row := FDrawRow;
796     FSelectedRow := FDrawRow;
797     FSelectedRecNo := aDataSet.RecNo;
798     SetupDrawPanel(FDrawRow);
799     end
800     else
801     Application.QueueAsyncCall(@DoSetupDrawPanel,0);
802     end
803     else
804     UpdateActive;
805     end;
806    
807     procedure TDBControlGrid.OnUpdateData(aDataSet: TDataSet);
808     begin
809     UpdateData;
810     end;
811    
812     procedure TDBControlGrid.SetDataSource(AValue: TDataSource);
813     begin
814     if AValue = FDatalink.Datasource then Exit;
815     FDataLink.DataSource := AValue;
816     UpdateActive;
817     end;
818    
819     procedure TDBControlGrid.SetDrawPanel(AValue: TWinControl);
820     var theForm: TWinControl;
821     begin
822     if FDrawPanel = AValue then Exit;
823     if FDrawPanel <> nil then
824     begin
825     RemoveFreeNotification(FDrawPanel);
826     FDrawPanel.RemoveAllHandlersOfObject(self);
827     theForm := Parent;
828 tony 29 while not ((theForm is TCustomForm) or (theForm is TCustomFrame))
829     and (theForm.Parent <> nil) do
830 tony 23 theForm := theForm.Parent;
831     FDrawPanel.Parent := theForm;
832     end;
833     FRowCache.ClearCache;
834     try
835     FDrawPanel := AValue;
836     if assigned(FDrawPanel) then
837     begin
838     FDrawPanel.Parent := self;
839     DefaultRowHeight := FDrawPanel.Height;
840     if csDesigning in ComponentState then
841     UpdateDrawPanelBounds(0)
842     else
843     FDrawPanel.Visible := false;
844     FRowCache.Height := FDrawPanel.Height;
845     FRowCache.Width := FDrawPanel.Width;
846 tony 29 FDrawPanel.AddHandlerOnResize(@OnDrawPanelResize);
847 tony 23 FreeNotification(FDrawPanel);
848     end;
849     except
850     FDrawPanel := nil;
851     raise;
852     end;
853     end;
854    
855     procedure TDBControlGrid.SetOptions(AValue: TPanelGridOptions);
856     begin
857     if FOptions = AValue then Exit;
858     FOptions := AValue;
859     if dgpIndicator in FOptions then
860     begin
861     FixedCols := 1;
862     ColWidths[0] := 12
863     end
864     else
865     FixedCols := 0;
866     end;
867    
868     procedure TDBControlGrid.SetupDrawPanel(aRow: integer);
869     begin
870 tony 45 if FDrawPanel = nil then Exit;
871 tony 23 if ValidDataSet and FRowCache.AlternateColor[FDataLink.DataSet.RecNo] then
872     FDrawPanel.Color := AlternateColor
873     else
874     FDrawPanel.Color := self.Color;
875     FDrawPanel.Visible := true;
876     UpdateDrawPanelBounds(aRow); {Position Draw Panel over expanded Row}
877     Invalidate;
878     end;
879    
880     function TDBControlGrid.UpdateGridCounts: Integer;
881     var
882     RecCount: Integer;
883     FRCount, FCCount: Integer;
884     begin
885     BeginUpdate;
886     try
887     FRCount := 0;
888     if dgpIndicator in FOptions then
889     FCCount := 1
890     else
891     FCCount := 0;
892     if FDataLink.Active then begin
893     UpdateBufferCount;
894     RecCount := FDataLink.RecordCount;
895     if RecCount<1 then
896     RecCount := 1;
897     end else begin
898     RecCount := 0;
899     if FRCount=0 then
900     // need to be large enough to hold indicator
901     // if there is one, and if there are no titles
902     RecCount := FCCount;
903     end;
904    
905     Inc(RecCount, FRCount);
906    
907     RowCount := RecCount;
908     FixedRows := FRCount;
909     Result := RowCount ;
910     finally
911     EndUpdate;
912     end;
913     end;
914    
915     procedure TDBControlGrid.UpdateBufferCount;
916     var
917     BCount: Integer;
918     begin
919     if FDataLink.Active then begin
920     BCount := GetBufferCount;
921     if BCount<1 then
922     BCount := 1;
923     FDataLink.BufferCount:= BCount;
924     end;
925     end;
926    
927     procedure TDBControlGrid.UpdateDrawPanelBounds(aRow: integer);
928     var R: TRect;
929     begin
930     R := Rect(0,0,0,0);
931     if assigned(FDrawPanel) and
932     (aRow >= 0) and (aRow < RowCount) then
933     begin
934     // Upper and Lower bounds for this row
935     ColRowToOffSet(False, True, aRow, R.Top, R.Bottom);
936     //Bounds for visible Column
937     ColRowToOffSet(True,True,ColCount-1,R.Left,R.RIght);
938     FDrawPanel.BoundsRect := R;
939     end;
940     end;
941    
942     procedure TDBControlGrid.UpdateScrollbarRange;
943     var
944     aRange, aPage, aPos: Integer;
945     ScrollInfo: TScrollInfo;
946     begin
947    
948     if not HandleAllocated then exit;
949    
950    
951     GetScrollBarParams(aRange, aPage, aPos);
952    
953     FillChar(ScrollInfo, SizeOf(ScrollInfo), 0);
954     ScrollInfo.cbSize := SizeOf(ScrollInfo);
955    
956     {TODO: try to move this out}
957     {$ifdef WINDOWS}
958     ScrollInfo.fMask := SIF_ALL or SIF_DISABLENOSCROLL;
959     ScrollInfo.ntrackPos := 0;
960     {$else}
961     ScrollInfo.fMask := SIF_ALL or SIF_UPDATEPOLICY;
962     //ScrollInfo.ntrackPos := SB_POLICY_CONTINUOUS;
963     ScrollInfo.ntrackPos := SB_POLICY_DISCONTINUOUS;
964     {$endif}
965     ScrollInfo.nMin := 0;
966     ScrollInfo.nMax := aRange;
967     ScrollInfo.nPos := Min(aPos,aRange-aPage);
968     ScrollInfo.nPage := aPage;
969     // the redraw argument of SetScrollInfo means under gtk
970     // if the scrollbar is visible or not, in windows it
971     // seems to mean if the scrollbar is redrawn or not
972     // to reflect the scrollbar changes made
973     SetScrollInfo(Handle, SB_VERT, ScrollInfo,
974     (ScrollBars in [ssBoth, ssVertical]) or
975     ((Scrollbars in [ssAutoVertical, ssAutoBoth]) and (aRange>aPAge))
976     );
977     FOldPosition := aPos;
978     end;
979    
980     procedure TDBControlGrid.WMVScroll(var Message: TLMVScroll);
981     var
982     IsSeq: boolean;
983     aPos, aRange, aPage: Integer;
984     DeltaRec: integer;
985    
986     function MaxPos: Integer;
987     begin
988     if IsSeq then
989     result := GetRecordCount - 1
990     else
991     result := 4;
992     end;
993    
994     procedure DsMoveBy(Delta: Integer);
995     begin
996     FDataLink.DataSet.MoveBy(Delta);
997     GetScrollbarParams(aRange, aPage, aPos);
998     end;
999    
1000     procedure DsGoto(BOF: boolean);
1001     begin
1002     if BOF then FDatalink.DataSet.First
1003     else FDataLink.DataSet.Last;
1004     GetScrollbarParams(aRange, aPage, aPos);
1005     end;
1006    
1007     function DsPos: boolean;
1008     begin
1009     result := false;
1010     aPos := Message.Pos;
1011     if aPos=FOldPosition then begin
1012     result := true;
1013     exit;
1014     end;
1015     if aPos>=MaxPos then
1016     dsGoto(False)
1017     else if aPos<=0 then
1018     dsGoto(True)
1019     else if IsSeq then
1020     FDatalink.DataSet.RecNo := aPos + 1
1021     else begin
1022     DeltaRec := Message.Pos - FOldPosition;
1023     if DeltaRec=0 then begin
1024     result := true;
1025     exit
1026     end
1027     else if DeltaRec<-1 then
1028     DsMoveBy(-VisibleRowCount)
1029     else if DeltaRec>1 then
1030     DsMoveBy(VisibleRowCount)
1031     else
1032     DsMoveBy(DeltaRec);
1033     end;
1034     end;
1035    
1036     begin
1037     if not FDatalink.Active or not assigned(FDataLink.DataSet) then exit;
1038    
1039     IsSeq := FDatalink.DataSet.IsSequenced and not FDataLink.DataSet.Filtered;
1040     case Message.ScrollCode of
1041     SB_TOP:
1042     DsGoto(True);
1043     SB_BOTTOM:
1044     DsGoto(False);
1045     SB_PAGEUP:
1046     DsMoveBy(-VisibleRowCount);
1047     SB_LINEUP:
1048     DsMoveBy(-1);
1049     SB_LINEDOWN:
1050     DsMoveBy(1);
1051     SB_PAGEDOWN:
1052     DsMoveBy(VisibleRowCount);
1053     SB_THUMBPOSITION:
1054     if DsPos then
1055     exit;
1056     SB_THUMBTRACK:
1057     if not (FDatalink.DataSet.IsSequenced) or DsPos then
1058     begin
1059     exit;
1060     end;
1061     else begin
1062     Exit;
1063     end;
1064     end;
1065    
1066     ScrollBarPosition(SB_VERT, aPos);
1067     FOldPosition:=aPos; end;
1068    
1069     function TDBControlGrid.ISEOF: boolean;
1070     begin
1071     with FDatalink do
1072     result := ValidDataSet and DataSet.EOF;
1073     end;
1074    
1075     function TDBControlGrid.ValidDataSet: boolean;
1076     begin
1077     result := FDatalink.Active And (FDatalink.DataSet<>nil)
1078     end;
1079    
1080     function TDBControlGrid.InsertCancelable: boolean;
1081     begin
1082     Result := ValidDataSet;
1083     if Result then
1084     with FDatalink.DataSet do
1085     Result := (State=dsInsert) and not Modified ;
1086     end;
1087    
1088     function TDBControlGrid.GetBufferCount: integer;
1089     begin
1090     Result := ClientHeight div DefaultRowHeight;
1091     end;
1092    
1093     procedure TDBControlGrid.DoEnter;
1094     begin
1095     inherited DoEnter;
1096     FWeHaveFocus := true;
1097     end;
1098    
1099     procedure TDBControlGrid.DoExit;
1100     begin
1101     FWeHaveFocus := false;
1102     if ValidDataSet and (dgpCancelOnExit in Options) and
1103     InsertCancelable then
1104     begin
1105     FDataLink.DataSet.Cancel;
1106     end;
1107     inherited DoExit;
1108     end;
1109    
1110     procedure TDBControlGrid.DoGridResize;
1111     begin
1112     if Columns.Count = 0 then Exit;
1113    
1114     if ColCount > 1 then
1115     Columns[0].Width := ClientWidth - ColWidths[0]
1116     else
1117     Columns[0].Width := ClientWidth;
1118    
1119     FRowCache.Width := Columns[0].Width;
1120     UpdateDrawPanelBounds(Row);
1121     end;
1122    
1123     procedure TDBControlGrid.DoOnResize;
1124     begin
1125     inherited DoOnResize;
1126     DoGridResize;
1127     end;
1128    
1129     procedure TDBControlGrid.DoScrollDataSet(Data: PtrInt);
1130     begin
1131     if AppDestroying in Application.Flags then Exit;
1132     FDataLink.DataSet.MoveBy(integer(Data) - FDataLink.DataSet.RecNo);
1133     end;
1134    
1135 tony 27 procedure TDBControlGrid.DoSelectNext(Data: PtrInt);
1136     begin
1137     FDataLink.DataSet.MoveBy(1);
1138     end;
1139    
1140 tony 23 procedure TDBControlGrid.DrawAllRows;
1141     begin
1142     inherited DrawAllRows;
1143     if ValidDataSet and FDatalink.DataSet.Active then
1144     begin
1145     if FInCacheRefresh and not FCacheRefreshQueued then
1146     {We are at the end of a cache refresh cycle}
1147     begin
1148     if FRequiredRecNo > 0 then
1149     begin
1150     if FRequiredRecNo <> FDataLink.DataSet.RecNo then
1151     Application.QueueAsyncCall(@DoScrollDataSet,FRequiredRecNo);
1152     FRequiredRecNo := 0;
1153     end
1154     else
1155     if FDrawRow <> FSelectedRow then
1156     Application.QueueAsyncCall(@DoMoveRecord,FSelectedRow);
1157     end;
1158     FInCacheRefresh := false;
1159     end;
1160     end;
1161    
1162     procedure TDBControlGrid.DrawRow(ARow: Integer);
1163     begin
1164     if (ARow>=FixedRows) and FDataLink.Active then
1165     FDrawingActiveRecord := (ARow = FDrawRow)
1166     else
1167     FDrawingActiveRecord := False;
1168     inherited DrawRow(ARow);
1169     end;
1170    
1171     procedure TDBControlGrid.DrawCell(aCol, aRow: Integer; aRect: TRect;
1172     aState: TGridDrawState);
1173    
1174     function GetDatasetState: TDataSetState;
1175     begin
1176     if ValidDataSet then
1177     result := FDataLink.DataSet.State
1178     else
1179     result := dsInactive;
1180     end;
1181    
1182     begin
1183     PrepareCanvas(aCol, aRow, aState);
1184    
1185     if aCol < FixedCols then
1186     DrawIndicator(Canvas,aRow, aRect,GetDataSetState,false)
1187     else
1188 tony 45 if (FDrawPanel = nil) or not FDataLink.Active then
1189     DrawFillRect(Canvas,aRect)
1190     else
1191 tony 23 if not FDrawingActiveRecord and FDataLink.Active then
1192     DoDrawRow(aRow,aRect,aState);
1193     {if we are drawing the active record then this is rendered by the Draw Panel
1194     i.e. a child control - so we need do nothing here}
1195    
1196     DrawCellGrid(aCol, aRow, aRect, aState);
1197     end;
1198    
1199     procedure TDBControlGrid.DrawIndicator(ACanvas: TCanvas; aRow: integer;
1200     R: TRect; Opt: TDataSetState; MultiSel: boolean);
1201     var
1202     dx,dy, x, y: Integer;
1203     procedure CenterY;
1204     begin
1205     y := R.Top + (R.Bottom-R.Top) div 2;
1206     end;
1207     procedure CenterX;
1208     begin
1209     X := R.Left + (R.Right-R.Left) div 2;
1210     end;
1211     procedure DrawEdit(clr: Tcolor);
1212     begin
1213     ACanvas.Pen.Color := clr;
1214     CenterY;
1215     CenterX;
1216     ACanvas.MoveTo(X-2, Y-Dy);
1217     ACanvas.LineTo(X+3, Y-Dy);
1218     ACanvas.MoveTo(X, Y-Dy);
1219     ACanvas.LineTo(X, Y+Dy);
1220     ACanvas.MoveTo(X-2, Y+Dy);
1221     ACanvas.LineTo(X+3, Y+Dy);
1222     end;
1223     procedure DrawBrowse;
1224     begin
1225     ACanvas.Brush.Color:=clBlack;
1226     ACanvas.Pen.Color:=clBlack;
1227     CenterY;
1228     x:= R.Left+3;
1229     if MultiSel then begin
1230     if BiDiMode = bdRightToLeft then begin
1231     ACanvas.Polyline([point(x+dx,y-dy), point(x,y),point(x+dx,y+dy), point(x+dx,y+dy-1)]);
1232     ACanvas.Polyline([point(x+dx,y-dy+1), point(x+1,y),point(x+dx,y+dy-1), point(x+dx,y+dy-2)]);
1233     CenterX;
1234     Dec(X,3);
1235     ACanvas.Ellipse(Rect(X+dx-2,Y-2,X+dx+2,Y+2));
1236     end else begin
1237     ACanvas.Polyline([point(x,y-dy), point(x+dx,y),point(x,y+dy), point(x,y+dy-1)]);
1238     ACanvas.Polyline([point(x,y-dy+1),point(x+dx-1,y),point(x, y+dy-1), point(x,y+dy-2)]);
1239     CenterX;
1240     Dec(X,3);
1241     ACanvas.Ellipse(Rect(X-2,Y-2,X+2,Y+2));
1242     end;
1243     end else begin
1244     if BiDiMode = bdRightToLeft then
1245     ACanvas.Polygon([point(x,y),point(x+dx,y-dy),point(x+dx, y+dy),point(x,y)])
1246     else
1247     ACanvas.Polygon([point(x,y-dy),point(x+dx,y),point(x, y+dy),point(x,y-dy)]);
1248     end;
1249     end;
1250    
1251     begin
1252     ACanvas.Brush.Color := FixedColor;
1253     ACanvas.FillRect(R);
1254     if aRow <> Row then Exit;
1255    
1256     dx := 6;
1257     dy := 6;
1258     case Opt of
1259     dsBrowse:
1260     DrawBrowse;
1261     dsEdit:
1262     if FDrawingActiveRecord then
1263     DrawEdit(clBlack)
1264     else
1265     DrawBrowse;
1266     dsInsert:
1267     if FDrawingActiveRecord then
1268     DrawEdit(clGreen)
1269     else
1270     DrawBrowse;
1271     else
1272     if MultiSel then begin
1273     ACanvas.Brush.Color:=clBlack;
1274     ACanvas.Pen.Color:=clBlack;
1275     CenterX;
1276     CenterY;
1277     ACanvas.Ellipse(Rect(X-3,Y-3,X+3,Y+3));
1278     end;
1279     end; end;
1280    
1281     procedure TDBControlGrid.GridMouseWheel(shift: TShiftState; Delta: Integer);
1282     begin
1283     inherited GridMouseWheel(shift, Delta);
1284     self.SetFocus;
1285     if ValidDataSet then
1286     FDataLink.DataSet.MoveBy(Delta);
1287     end;
1288    
1289     procedure TDBControlGrid.KeyDown(var Key: Word; Shift: TShiftState);
1290     type
1291     TOperation=(opMoveBy,opCancel,opAppend,opInsert,opDelete);
1292    
1293     procedure DoOnKeyDown;
1294     begin
1295     if Assigned(OnKeyDown) then
1296     OnKeyDown(Self, Key, Shift);
1297     end;
1298    
1299     procedure DoOperation(AOper: TOperation; Arg: Integer = 0);
1300     begin
1301     self.SetFocus;
1302     case AOper of
1303     opMoveBy:
1304     FDatalink.DataSet.MoveBy(Arg);
1305     opCancel:
1306     begin
1307     FDatalink.Dataset.Cancel;
1308     end;
1309     opAppend:
1310     FDatalink.Dataset.Append;
1311     opInsert:
1312     FDatalink.Dataset.Insert;
1313     opDelete:
1314     FDatalink.Dataset.Delete;
1315     end;
1316     end;
1317    
1318     function doVKDown: boolean;
1319     begin
1320     if InsertCancelable then
1321     begin
1322     if IsEOF then
1323     result:=true
1324     else begin
1325     doOperation(opCancel);
1326     result := false;
1327     end;
1328     end else begin
1329     result:=false;
1330     doOperation(opMoveBy, 1);
1331     if GridCanModify and FDataLink.EOF then begin
1332     if not (dgpDisableInsert in Options) then
1333     doOperation(opAppend);
1334     end
1335     end;
1336     end;
1337    
1338     function DoVKUP: boolean;
1339     begin
1340     if InsertCancelable then
1341     doOperation(opCancel)
1342     else begin
1343     doOperation(opMoveBy, -1);
1344     end;
1345     result := FDatalink.DataSet.BOF;
1346     end;
1347    
1348    
1349    
1350     begin
1351     case Key of
1352     VK_DOWN:
1353     begin
1354     DoOnKeyDown;
1355     if (Key<>0) and ValidDataset then begin
1356     doVKDown;
1357     Key := 0;
1358     end;
1359     end;
1360    
1361     VK_UP:
1362     begin
1363     doOnKeyDown;
1364     if (Key<>0) and ValidDataset then begin
1365     doVKUp;
1366     key := 0;
1367     end;
1368     end;
1369    
1370     VK_NEXT:
1371     begin
1372     doOnKeyDown;
1373     if (Key<>0) and ValidDataset then begin
1374     doOperation(opMoveBy, VisibleRowCount);
1375     Key := 0;
1376     end;
1377     end;
1378    
1379     VK_PRIOR:
1380     begin
1381     doOnKeyDown;
1382     if (Key<>0) and ValidDataset then begin
1383     doOperation(opMoveBy, -VisibleRowCount);
1384     key := 0;
1385     end;
1386     end;
1387    
1388     VK_ESCAPE:
1389     begin
1390     doOnKeyDown;
1391     if ValidDataSet then
1392     doOperation(opCancel);
1393     end;
1394    
1395     VK_HOME:
1396     begin
1397     doOnKeyDown;
1398     if (Key<>0) and ValidDataSet then
1399     begin
1400     if ssCTRL in Shift then
1401     begin
1402     FDataLink.DataSet.First;
1403     Key:=0;
1404     end;
1405     end;
1406     end;
1407    
1408     VK_END:
1409     begin
1410     doOnKeyDown;
1411     if Key<>0 then begin
1412     if ValidDataSet then
1413     begin
1414     if ssCTRL in shift then
1415     begin
1416     FDatalink.DataSet.Last;
1417     Key:=0;
1418     end;
1419     end;
1420     end;
1421     end;
1422    
1423     end;
1424     end;
1425    
1426     procedure TDBControlGrid.LinkActive(Value: Boolean);
1427     begin
1428     if not Value then
1429     begin
1430     FRowCache.ClearCache;
1431     FInCacheRefresh := false;
1432     FCacheRefreshQueued := false;
1433     Row := FixedRows;
1434 tony 35 FDrawingActiveRecord := false;
1435     FSelectedRecNo := 0;
1436     FSelectedRow := 0;
1437     FRequiredRecNo := 0;
1438 tony 23 end;
1439     FRowCache.UseAlternateColors := AlternateColor <> Color;
1440     FRowCache.AltColorStartNormal := AltColorStartNormal;
1441     FLastRecordCount := 0;
1442     LayoutChanged;
1443     if Value then
1444     begin
1445     { The problem being solved here is that TDataSet does not readily tell us
1446     when a record is deleted. We get a DataSetChanged event - but this can
1447     occur for many reasons. Only by monitoring the record count accurately
1448     can be determine when a record is deleted. To do this we need to scroll
1449     the whole dataset to the end when the dataset is activated. Not desirable
1450     with large datasets - but a fix to TDataSet is needed to avoid this.
1451     }
1452     FDataLink.DataSet.DisableControls;
1453     try
1454     FDataLink.DataSet.Last;
1455     FLastRecordCount := FDataLink.DataSet.RecordCount;
1456     if not FDefaultPositionAtEnd then
1457     FDataLink.DataSet.First;
1458     FRequiredRecNo := FDataLink.DataSet.RecNo;
1459     finally
1460     FDataLink.DataSet.EnableControls;
1461     end;
1462     end;
1463     end;
1464    
1465     procedure TDBControlGrid.LayoutChanged;
1466     begin
1467     if csDestroying in ComponentState then
1468     exit;
1469     BeginUpdate;
1470     try
1471     if UpdateGridCounts=0 then
1472     EmptyGrid;
1473     finally
1474     EndUpdate;
1475     end;
1476     UpdateScrollbarRange;
1477     end;
1478    
1479     procedure TDBControlGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
1480     Y: Integer);
1481     var
1482     Gz: TGridZone;
1483     P: TPoint;
1484     procedure doMouseDown;
1485     begin
1486     // if not Focused then
1487     // SetFocus;
1488     if assigned(OnMouseDown) then
1489     OnMouseDown(Self, Button, Shift, X, Y);
1490     end;
1491     procedure doInherited;
1492     begin
1493     inherited MouseDown(Button, Shift, X, Y);
1494     end;
1495     procedure doMoveBy;
1496     begin
1497     FDatalink.DataSet.MoveBy(P.Y - Row);
1498     end;
1499     procedure doMoveToColumn;
1500     begin
1501     Col := P.X;
1502     end;
1503     procedure DoCancel;
1504     begin
1505     FDatalink.Dataset.cancel;
1506     end;
1507     begin
1508     if (csDesigning in componentState) or not ValidDataSet then begin
1509     exit;
1510     end;
1511     self.SetFocus;
1512    
1513     { if not MouseButtonAllowed(Button) then begin
1514     doInherited;
1515     exit;
1516     end;}
1517    
1518     Gz:=MouseToGridZone(X,Y);
1519     CacheMouseDown(X,Y);
1520     case Gz of
1521     gzInvalid:
1522     doMouseDown;
1523    
1524     gzFixedCells, gzFixedCols:
1525     doInherited;
1526     else
1527     begin
1528    
1529     P:=MouseToCell(Point(X,Y));
1530     if Gz=gzFixedRows then
1531     P.X := Col;
1532    
1533     if P.Y=Row then begin
1534     //doAcceptValue;
1535    
1536     if not (ssCtrl in Shift) then
1537     begin
1538     if gz=gzFixedRows then
1539     doMouseDown
1540     else
1541     doInherited;
1542     end;
1543    
1544     end else begin
1545     doMouseDown;
1546     if ValidDataSet then begin
1547     if InsertCancelable and IsEOF then
1548     doCancel;
1549     doMoveBy;
1550     end;
1551     end;
1552     end;
1553     end;
1554     end;
1555    
1556     procedure TDBControlGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
1557     Y: Integer);
1558     begin
1559     inherited MouseUp(Button, Shift, X, Y);
1560     FLastMouse.X := X;
1561     FLastMouse.Y := Y;
1562     FLastMouseButton := Button;
1563     FLastMouseShiftState := Shift;
1564     Application.QueueAsyncCall(@DoSendMouseClicks,0);
1565     end;
1566    
1567     procedure TDBControlGrid.MoveSelection;
1568     begin
1569     inherited MoveSelection;
1570     InvalidateRow(Row);
1571     end;
1572    
1573     procedure TDBControlGrid.Notification(AComponent: TComponent;
1574     Operation: TOperation);
1575     begin
1576     inherited Notification(AComponent, Operation);
1577     if (Operation = opRemove) and
1578     (AComponent = FDrawPanel) then FDrawPanel := nil;
1579     end;
1580    
1581     procedure TDBControlGrid.PrepareCanvas(aCol, aRow: Integer; aState: TGridDrawState
1582     );
1583     begin
1584     inherited PrepareCanvas(aCol, aRow, aState);
1585    
1586     if gdFixed in aState then
1587     begin
1588     if gdHot in aState then
1589     Canvas.Brush.Color := FixedHotColor
1590     else
1591     Canvas.Brush.Color := GetColumnColor(aCol, gdFixed in AState);
1592     end;
1593    
1594     if (not FDatalink.Active) and ((gdSelected in aState) or (gdFocused in aState)) then
1595     Canvas.Brush.Color := Self.Color;
1596    
1597     end;
1598    
1599     procedure TDBControlGrid.ResetSizes;
1600     begin
1601     LayoutChanged;
1602     inherited ResetSizes;
1603     DoGridResize;
1604     end;
1605    
1606     procedure TDBControlGrid.SetColor(Value: TColor);
1607     begin
1608     inherited SetColor(Value);
1609     if (csDesigning in ComponentState) and assigned(FDrawPaneL) then
1610     FDrawPanel.Color := Value;
1611     end;
1612    
1613     procedure TDBControlGrid.UpdateActive;
1614     var
1615     PrevRow: Integer;
1616     begin
1617     if (csDestroying in ComponentState) or
1618     (FDatalink=nil) or (not FDatalink.Active) or
1619     (FDatalink.ActiveRecord<0) then
1620     exit;
1621    
1622     FDrawRow := FixedRows + FDataLink.ActiveRecord;
1623     FSelectedRecNo := FDataLink.DataSet.RecNo;
1624     PrevRow := Row;
1625     Row := FDrawRow;
1626     if not FInCacheRefresh then
1627 tony 29 begin
1628 tony 23 FSelectedRow := FDrawRow;
1629 tony 29 if FDatalink.DataSet.State <> dsInsert then
1630     FRowCache.InvalidateRowImage(FSelectedRecNo);
1631     end;
1632 tony 23 InvalidateRow(PrevRow);
1633     SetupDrawPanel(FDrawRow);
1634     end;
1635    
1636     procedure TDBControlGrid.UpdateData;
1637     begin
1638     FModified := false;
1639     end;
1640    
1641     procedure TDBControlGrid.UpdateShowing;
1642     begin
1643     inherited UpdateShowing;
1644     DoGridResize
1645     end;
1646    
1647     procedure TDBControlGrid.UpdateVertScrollbar(const aVisible: boolean;
1648     const aRange, aPage, aPos: Integer);
1649     begin
1650     UpdateScrollbarRange;
1651     end;
1652    
1653     constructor TDBControlGrid.Create(AOwner: TComponent);
1654     begin
1655     inherited Create(AOwner);
1656     FDataLink := TDBControlGridDataLink.Create;//(Self);
1657     FRowCache := TRowCache.Create;
1658     FDataLink.OnRecordChanged:=@OnRecordChanged;
1659     FDataLink.OnDatasetChanged:=@OnDataSetChanged;
1660     FDataLink.OnDataSetOpen:=@OnDataSetOpen;
1661     FDataLink.OnDataSetClose:=@OnDataSetClose;
1662     FDataLink.OnNewDataSet:=@OnNewDataSet;
1663     FDataLink.OnInvalidDataSet:=@OnInvalidDataset;
1664     FDataLink.OnInvalidDataSource:=@OnInvalidDataSource;
1665     FDataLink.OnDataSetScrolled:=@OnDataSetScrolled;
1666     FDataLink.OnLayoutChanged:=@OnLayoutChanged;
1667     FDataLink.OnEditingChanged:=@OnEditingChanged;
1668     FDataLink.OnUpdateData:=@OnUpdateData;
1669     FDataLink.OnCheckBrowseMode := @OnCheckBrowseMode;
1670     FDataLink.VisualControl:= True;
1671     ScrollBars := ssAutoVertical;
1672     FOptions := [dgpIndicator];
1673     FixedCols := 1;
1674     ColCount := 1;
1675     FixedRows := 0;
1676     RowCount := 1;
1677     ColWidths[0] := 12;
1678     Columns.Add.ReadOnly := true; {Add Dummy Column for Panel}
1679     DoGridResize;
1680     if not (csDesigning in ComponentState) then
1681     Application.AddOnKeyDownBeforeHandler(@KeyDownHandler,false);
1682     end;
1683    
1684     destructor TDBControlGrid.Destroy;
1685     begin
1686     if assigned(FDataLink) then
1687     begin
1688     FDataLink.OnDataSetChanged:=nil;
1689     FDataLink.OnRecordChanged:=nil;
1690     FDataLink.Free;
1691     end;
1692     if assigned(FRowCache) then FRowCache.Free;
1693 tony 315 if not (csDesigning in ComponentState) then
1694     Application.RemoveOnKeyDownBeforeHandler( @KeyDownHandler );
1695 tony 80 Application.RemoveAsyncCalls(self);
1696 tony 23 inherited Destroy;
1697     end;
1698    
1699     function TDBControlGrid.MouseToRecordOffset(const x, y: Integer;
1700     out RecordOffset: Integer): TGridZone;
1701     var
1702     aCol,aRow: Integer;
1703     begin
1704     Result := MouseToGridZone(x, y);
1705    
1706     RecordOffset := 0;
1707    
1708     if (Result=gzInvalid) or (Result=gzFixedCells) then
1709     exit;
1710    
1711     MouseToCell(x, y, aCol, aRow);
1712    
1713     if (Result=gzFixedRows) or (Result=gzNormal) then
1714     RecordOffset := aRow - Row;
1715    
1716     if (Result=gzFixedCols) or (Result=gzNormal) then begin
1717     aRow := ColumnIndexFromGridColumn(aCol);
1718     end;
1719     end;
1720    
1721     function TDBControlGrid.ExecuteAction(AAction: TBasicAction): Boolean;
1722     begin
1723     Result := (DataLink <> nil)
1724     and DataLink.ExecuteAction(AAction);
1725     end;
1726    
1727     function TDBControlGrid.UpdateAction(AAction: TBasicAction): Boolean;
1728     begin
1729     Result := (DataLink <> nil)
1730     and DataLink.UpdateAction(AAction);
1731     end;
1732    
1733     end.
1734 tony 45