ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/DBControlGrid.pas
Revision: 31
Committed: Tue Jul 14 15:31:25 2015 UTC (9 years, 5 months ago) by tony
Content type: text/x-pascal
File size: 45661 byte(s)
Log Message:
Committing updates for Release R1-3-0

File Contents

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