ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/DBControlGrid.pas
Revision: 27
Committed: Tue Apr 14 13:10:23 2015 UTC (9 years, 7 months ago) by tony
Content type: text/x-pascal
File size: 44179 byte(s)
Log Message:
Committing updates for Release R1-2-3

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