ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/ibcontrols/DBControlGrid.pas
Revision: 143
Committed: Fri Feb 23 12:11:21 2018 UTC (6 years, 9 months ago) by tony
Content type: text/x-pascal
Original Path: ibx/trunk/ibcontrols/DBControlGrid.pas
File size: 46582 byte(s)
Log Message:
Fixes Merged

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