ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/ibcontrols/DBControlGrid.pas
Revision: 35
Committed: Tue Jan 26 14:38:47 2016 UTC (8 years, 10 months ago) by tony
Content type: text/x-pascal
Original Path: ibx/trunk/ibcontrols/DBControlGrid.pas
File size: 46005 byte(s)
Log Message:
Committing updates for Release R1-3-2

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 tony 35 SetLength(FList,0);
417 tony 23 end;
418    
419     function TRowCache.Add2Cache(RecNo: Longint; Control: TWinControl): TBitmap;
420     var i: integer;
421     begin
422     Dec(RecNo); {Adust to zero base}
423     ExtendCache(RecNo + 1);
424     FList[RecNo].FState := rcPresent;
425     FList[RecNo].FBitmap := Render(Control);
426     Result := FList[RecNo].FBitmap;
427     end;
428    
429     function TRowCache.GetRowImage(RecNo, Offset: integer): TBitmap;
430     begin
431     Result := nil;
432     Dec(RecNo); {adjust to zero base}
433     if (RecNo < 0) or (RecNo >= Length(FList)) then
434     Exit;
435    
436     if Offset >= 0 then
437     repeat
438     while (RecNo < Length(FList)) and (FList[RecNo].FState = rcDeleted) do
439     Inc(RecNo);
440    
441     if RecNo >= Length(FList) then
442     Exit;
443    
444     if Offset = 0 then
445     begin
446     if FList[RecNo].FState = rcPresent then
447     Result := FList[RecNo].FBitmap;
448     Exit;
449     end;
450     Inc(RecNo);
451     Dec(Offset);
452     until false
453     else
454     repeat
455     Inc(Offset);
456     Dec(RecNo);
457     while (RecNo > 0) and (FList[RecNo].FState = rcDeleted) do
458     Dec(RecNo);
459    
460     if RecNo < 0 then
461     Exit;
462    
463     if Offset = 0 then
464     begin
465     if FList[RecNo].FState = rcPresent then
466     Result := FList[RecNo].FBitmap;
467     Exit;
468     end;
469     until false;
470     end;
471    
472 tony 29 procedure TRowCache.InvalidateRowImage(RecNo: integer);
473     begin
474     Dec(RecNo); {adjust to zero base}
475     if (RecNo < 0) or (RecNo >= Length(FList)) then
476     Exit;
477    
478     if FList[RecNo].FState = rcPresent then
479     begin
480     FList[RecNo].FBitmap.Free;
481     FList[RecNo].FState := rcEmpty;
482     end;
483     end;
484    
485 tony 23 function TRowCache.IsEmpty(RecNo: integer): boolean;
486     begin
487     Dec(RecNo);
488     Result := (RecNo < 0) or (RecNo >= Length(FList)) or (FList[RecNo].FState = rcEmpty);
489     end;
490    
491     procedure TRowCache.MarkAsDeleted(RecNo: integer);
492     var altColor: boolean;
493     i: integer;
494     begin
495     Dec(RecNo); {adjust to zero base}
496     if (RecNo < 0) or (RecNo >= Length(FList)) then
497     Exit;
498    
499     FList[RecNo].FState := rcDeleted;
500     if not UseAlternateColors then
501     Exit;
502    
503     {Reset Alternate Colours}
504    
505     if RecNo = 0 then
506     altColor := not AltColorStartNormal
507     else
508     altColor := not FList[RecNo-1].FAlternateColor;
509    
510     for i := RecNo + 1 to Length(FList) - 1 do
511     begin
512     if FList[i].FState <> rcDeleted then
513     begin
514     FList[i].FAlternateColor := altColor;
515     altColor := not altColor;
516     if FList[i].FState = rcPresent then
517     begin
518     FList[i].FBitmap.Free;
519     FList[i].FState := rcEmpty;
520     end;
521     end;
522     end;
523     end;
524    
525     { TDBControlGrid }
526    
527 tony 31 function TDBControlGrid.ActiveControl: TControl;
528     var AParent: TWinControl;
529     begin
530     Result := nil;
531     AParent := Parent;
532     while (AParent <> nil) and not (AParent is TCustomForm) do
533     AParent := AParent.Parent;
534     if (AParent <> nil) and (AParent is TCustomForm)then
535     Result := TCustomForm(AParent).ActiveControl;
536     end;
537    
538 tony 23 procedure TDBControlGrid.EmptyGrid;
539     var
540     OldFixedRows: Integer;
541     begin
542     OldFixedRows := FixedRows;
543     Clear;
544 tony 35 FRowCache.ClearCache;
545 tony 23 RowCount := OldFixedRows + 1;
546     if dgpIndicator in FOptions then
547     ColWidths[0]:=12;
548     if assigned(FDrawPanel) then
549     FDrawPanel.Visible := false;
550     end;
551    
552     function TDBControlGrid.GetDataSource: TDataSource;
553     begin
554     Result:= FDataLink.DataSource;
555     end;
556    
557     function TDBControlGrid.GetRecordCount: Integer;
558     begin
559     if assigned(FDataLink.DataSet) then
560     result := FDataLink.DataSet.RecordCount
561     else
562     result := 0;
563     end;
564    
565     procedure TDBControlGrid.GetScrollbarParams(out aRange, aPage, aPos: Integer);
566     begin
567     if (FDatalink<>nil) and (FDataLink.DataSet <> nil) and FDatalink.Active then begin
568     if FDatalink.dataset.IsSequenced then begin
569     aRange := GetRecordCount + VisibleRowCount - 1;
570     aPage := VisibleRowCount;
571     if aPage<1 then aPage := 1;
572     if FDatalink.BOF then aPos := 0 else
573     if FDatalink.EOF then aPos := aRange
574     else
575     aPos := FDataLink.DataSet.RecNo - 1; // RecNo is 1 based
576     if aPos<0 then aPos:=0;
577     end else begin
578     aRange := 6;
579     aPage := 2;
580     if FDatalink.EOF then aPos := 4 else
581     if FDatalink.BOF then aPos := 0
582     else aPos := 2;
583     end;
584     end else begin
585     aRange := 0;
586     aPage := 0;
587     aPos := 0;
588     end;
589     end;
590    
591     function TDBControlGrid.GridCanModify: boolean;
592     begin
593     result := not FDataLink.ReadOnly
594     and ValidDataSet and FDatalink.DataSet.CanModify;
595     end;
596    
597    
598     procedure TDBControlGrid.DoDrawRow(aRow: integer; aRect: TRect;
599     aState: TGridDrawState);
600     var CachedRow: TBitmap;
601     begin
602     CachedRow := FRowCache.GetRowImage(FSelectedRecNo,aRow-FDrawRow);
603     {if the row is in the cache then draw it - otherwise schedule a cache refresh cycle}
604     if CachedRow = nil then
605     begin
606     if not FCacheRefreshQueued then
607     begin
608     FCacheRefreshQueued := true;
609     Application.QueueAsyncCall(@DoMoveRecord,PtrInt(aRow));
610     end
611     end
612     else
613     Canvas.Draw(aRect.Left,aRect.Top,CachedRow)
614     end;
615    
616     procedure TDBControlGrid.DoMoveRecord(Data: PtrInt);
617     var aRow: integer;
618     begin
619     if AppDestroying in Application.Flags then Exit;
620    
621     FCacheRefreshQueued := false;
622     aRow := integer(Data);
623     FInCacheRefresh := true;
624     if assigned(FDataLink.DataSet) then
625 tony 35 FDatalink.DataSet.MoveBy(aRow - FDrawRow);
626 tony 23 end;
627    
628     procedure TDBControlGrid.DoSetupDrawPanel(Data: PtrInt);
629     begin
630     if AppDestroying in Application.Flags then Exit;
631     SetupDrawPanel(FDrawRow);
632     end;
633    
634     procedure TDBControlGrid.DoSendMouseClicks(Data: PtrInt);
635     var P: TPoint;
636     Control: TControl;
637     begin
638     if AppDestroying in Application.Flags then Exit;
639    
640     if assigned(FDrawPanel) and (FLastMouse.X <> 0) then
641     begin
642     P := ClientToScreen(FLastMouse);
643     Control := FindControlAtPosition(P,false);
644     if (Control <> nil) and (Control is TWinControl) then
645     TWinControl(Control).SetFocus
646     else
647     Control := FDrawPanel;
648    
649     P := Control.ScreenToClient(P);
650    
651     LCLSendMouseDownMsg(Control,P.X,P.Y,FLastMouseButton,FLastMouseShiftState);
652     LCLSendMouseUpMsg(Control,P.X,P.Y, FLastMouseButton,FLastMouseShiftState);
653    
654     end;
655     FLastMouse.X := 0;
656     end;
657    
658     procedure TDBControlGrid.KeyDownHandler(Sender: TObject; var Key: Word;
659     Shift: TShiftState);
660     var Done: boolean;
661 tony 31 AControl: TControl;
662 tony 23 begin
663     if Visible and assigned(FDrawPanel) and FDrawPanel.Visible and FWeHaveFocus then
664     begin
665 tony 31 AControl := ActiveControl;
666     if (AControl <> nil) and (AControl is TCustomComboBox)
667     and ((Key in [VK_UP,VK_DOWN]) or
668     (TCustomComboBox(AControl).DroppedDown and (Key = VK_RETURN)) or
669     ((TCustomComboBox(AControl).Text <> '') and (Key = VK_ESCAPE))) then
670     Exit; {ignore these keys if we are in a combobox}
671    
672     if (AControl <> nil) and (AControl is TCustomMemo)
673 tony 35 and (Key in [VK_RETURN,VK_UP,VK_DOWN]) then Exit; {Ignore Return in a CustomMemo}
674 tony 31
675 tony 23 Done := false;
676     if assigned(FOnKeyDownHander) then
677     OnKeyDownHander(Sender,Key,Shift,Done);
678     if Done then Exit;
679    
680     KeyDown(Key,Shift)
681     end;
682     end;
683    
684     procedure TDBControlGrid.OnRecordChanged(Field: TField);
685     begin
686     UpdateActive
687     end;
688    
689     procedure TDBControlGrid.OnCheckBrowseMode(aDataSet: TDataSet);
690     var RecNo: integer;
691     begin
692     if assigned(FDrawPanel) and (aDataSet.RecNo > 0)
693     and (FModified or (FRowCache.IsEmpty(aDataSet.RecNo))) then
694     begin
695     RecNo := aDataSet.RecNo;
696     Application.ProcessMessages; {A couple of trips round the message loop seems to be necessary}
697     Application.ProcessMessages;
698     if RecNo = aDataSet.RecNo then {Guard against sudden changes}
699     FRowCache.Add2Cache(aDataSet.RecNo,FDrawPanel);
700     end;
701     end;
702    
703     procedure TDBControlGrid.OnDataSetChanged(aDataSet: TDataSet);
704     begin
705 tony 35 if aDataSet.State = dsBrowse then
706 tony 23 begin
707 tony 35 if GetRecordCount = 0 then
708     begin
709     {Must be closed/reopened}
710     FRowCache.ClearCache;
711     FSelectedRow := 0;
712     end
713     else
714     if FLastRecordCount > GetRecordCount then
715     begin
716     {must be delete}
717     FRowCache.MarkAsDeleted(FSelectedRecNo);
718     Dec(FSelectedRow);
719     end;
720 tony 23 LayoutChanged;
721     end;
722     FLastRecordCount := GetRecordCount;
723     if aDataSet.State = dsInsert then
724 tony 27 begin
725 tony 23 FRequiredRecNo := aDataSet.RecNo + 1;
726 tony 27 Application.QueueAsyncCall(@DoSelectNext,0);
727     end;
728 tony 23 UpdateActive
729     end;
730    
731     procedure TDBControlGrid.OnDataSetOpen(aDataSet: TDataSet);
732     begin
733     LinkActive(true);
734     UpdateActive;
735     end;
736    
737     procedure TDBControlGrid.OnDataSetClose(aDataSet: TDataSet);
738     begin
739     LinkActive(false);
740     end;
741    
742     procedure TDBControlGrid.OnDrawPanelResize(Sender: TObject);
743     begin
744     FRowCache.Height := FDrawPanel.Height;
745     DefaultRowHeight := FDrawPanel.Height;
746     end;
747    
748     procedure TDBControlGrid.OnEditingChanged(aDataSet: TDataSet);
749     begin
750     FModified := true;
751     end;
752    
753     procedure TDBControlGrid.OnInvalidDataSet(aDataSet: TDataSet);
754     begin
755     LinkActive(False);
756     end;
757    
758     procedure TDBControlGrid.OnInvalidDataSource(aDataSet: TDataset);
759     begin
760     LinkActive(False);
761     end;
762    
763     procedure TDBControlGrid.OnLayoutChanged(aDataSet: TDataSet);
764     begin
765     LayoutChanged;
766     end;
767    
768     procedure TDBControlGrid.OnNewDataSet(aDataSet: TDataset);
769     begin
770     LinkActive(True);
771     UpdateActive;
772     end;
773    
774     procedure TDBControlGrid.OnDataSetScrolled(aDataSet: TDataSet; Distance: Integer);
775     begin
776     UpdateScrollBarRange;
777     if Distance <> 0 then
778     begin
779     FDrawRow := FixedRows + FDataLink.ActiveRecord;
780    
781     if not FInCacheRefresh then
782     begin
783     Row := FDrawRow;
784     FSelectedRow := FDrawRow;
785     FSelectedRecNo := aDataSet.RecNo;
786     SetupDrawPanel(FDrawRow);
787     end
788     else
789     Application.QueueAsyncCall(@DoSetupDrawPanel,0);
790     end
791     else
792     UpdateActive;
793     end;
794    
795     procedure TDBControlGrid.OnUpdateData(aDataSet: TDataSet);
796     begin
797     UpdateData;
798     end;
799    
800     procedure TDBControlGrid.SetDataSource(AValue: TDataSource);
801     begin
802     if AValue = FDatalink.Datasource then Exit;
803     FDataLink.DataSource := AValue;
804     UpdateActive;
805     end;
806    
807     procedure TDBControlGrid.SetDrawPanel(AValue: TWinControl);
808     var theForm: TWinControl;
809     begin
810     if FDrawPanel = AValue then Exit;
811     if FDrawPanel <> nil then
812     begin
813     RemoveFreeNotification(FDrawPanel);
814     FDrawPanel.RemoveAllHandlersOfObject(self);
815     theForm := Parent;
816 tony 29 while not ((theForm is TCustomForm) or (theForm is TCustomFrame))
817     and (theForm.Parent <> nil) do
818 tony 23 theForm := theForm.Parent;
819     FDrawPanel.Parent := theForm;
820     end;
821     FRowCache.ClearCache;
822     try
823     FDrawPanel := AValue;
824     if assigned(FDrawPanel) then
825     begin
826     FDrawPanel.Parent := self;
827     DefaultRowHeight := FDrawPanel.Height;
828     if csDesigning in ComponentState then
829     UpdateDrawPanelBounds(0)
830     else
831     FDrawPanel.Visible := false;
832     FRowCache.Height := FDrawPanel.Height;
833     FRowCache.Width := FDrawPanel.Width;
834 tony 29 FDrawPanel.AddHandlerOnResize(@OnDrawPanelResize);
835 tony 23 FreeNotification(FDrawPanel);
836     end;
837     except
838     FDrawPanel := nil;
839     raise;
840     end;
841     end;
842    
843     procedure TDBControlGrid.SetOptions(AValue: TPanelGridOptions);
844     begin
845     if FOptions = AValue then Exit;
846     FOptions := AValue;
847     if dgpIndicator in FOptions then
848     begin
849     FixedCols := 1;
850     ColWidths[0] := 12
851     end
852     else
853     FixedCols := 0;
854     end;
855    
856     procedure TDBControlGrid.SetupDrawPanel(aRow: integer);
857     begin
858     if ValidDataSet and FRowCache.AlternateColor[FDataLink.DataSet.RecNo] then
859     FDrawPanel.Color := AlternateColor
860     else
861     FDrawPanel.Color := self.Color;
862     FDrawPanel.Visible := true;
863     UpdateDrawPanelBounds(aRow); {Position Draw Panel over expanded Row}
864     Invalidate;
865     end;
866    
867     function TDBControlGrid.UpdateGridCounts: Integer;
868     var
869     RecCount: Integer;
870     FRCount, FCCount: Integer;
871     begin
872     BeginUpdate;
873     try
874     FRCount := 0;
875     if dgpIndicator in FOptions then
876     FCCount := 1
877     else
878     FCCount := 0;
879     if FDataLink.Active then begin
880     UpdateBufferCount;
881     RecCount := FDataLink.RecordCount;
882     if RecCount<1 then
883     RecCount := 1;
884     end else begin
885     RecCount := 0;
886     if FRCount=0 then
887     // need to be large enough to hold indicator
888     // if there is one, and if there are no titles
889     RecCount := FCCount;
890     end;
891    
892     Inc(RecCount, FRCount);
893    
894     RowCount := RecCount;
895     FixedRows := FRCount;
896     Result := RowCount ;
897     finally
898     EndUpdate;
899     end;
900     end;
901    
902     procedure TDBControlGrid.UpdateBufferCount;
903     var
904     BCount: Integer;
905     begin
906     if FDataLink.Active then begin
907     BCount := GetBufferCount;
908     if BCount<1 then
909     BCount := 1;
910     FDataLink.BufferCount:= BCount;
911     end;
912     end;
913    
914     procedure TDBControlGrid.UpdateDrawPanelBounds(aRow: integer);
915     var R: TRect;
916     begin
917     R := Rect(0,0,0,0);
918     if assigned(FDrawPanel) and
919     (aRow >= 0) and (aRow < RowCount) then
920     begin
921     // Upper and Lower bounds for this row
922     ColRowToOffSet(False, True, aRow, R.Top, R.Bottom);
923     //Bounds for visible Column
924     ColRowToOffSet(True,True,ColCount-1,R.Left,R.RIght);
925     FDrawPanel.BoundsRect := R;
926     end;
927     end;
928    
929     procedure TDBControlGrid.UpdateScrollbarRange;
930     var
931     aRange, aPage, aPos: Integer;
932     ScrollInfo: TScrollInfo;
933     begin
934    
935     if not HandleAllocated then exit;
936    
937    
938     GetScrollBarParams(aRange, aPage, aPos);
939    
940     FillChar(ScrollInfo, SizeOf(ScrollInfo), 0);
941     ScrollInfo.cbSize := SizeOf(ScrollInfo);
942    
943     {TODO: try to move this out}
944     {$ifdef WINDOWS}
945     ScrollInfo.fMask := SIF_ALL or SIF_DISABLENOSCROLL;
946     ScrollInfo.ntrackPos := 0;
947     {$else}
948     ScrollInfo.fMask := SIF_ALL or SIF_UPDATEPOLICY;
949     //ScrollInfo.ntrackPos := SB_POLICY_CONTINUOUS;
950     ScrollInfo.ntrackPos := SB_POLICY_DISCONTINUOUS;
951     {$endif}
952     ScrollInfo.nMin := 0;
953     ScrollInfo.nMax := aRange;
954     ScrollInfo.nPos := Min(aPos,aRange-aPage);
955     ScrollInfo.nPage := aPage;
956     // the redraw argument of SetScrollInfo means under gtk
957     // if the scrollbar is visible or not, in windows it
958     // seems to mean if the scrollbar is redrawn or not
959     // to reflect the scrollbar changes made
960     SetScrollInfo(Handle, SB_VERT, ScrollInfo,
961     (ScrollBars in [ssBoth, ssVertical]) or
962     ((Scrollbars in [ssAutoVertical, ssAutoBoth]) and (aRange>aPAge))
963     );
964     FOldPosition := aPos;
965     end;
966    
967     procedure TDBControlGrid.WMVScroll(var Message: TLMVScroll);
968     var
969     IsSeq: boolean;
970     aPos, aRange, aPage: Integer;
971     DeltaRec: integer;
972    
973     function MaxPos: Integer;
974     begin
975     if IsSeq then
976     result := GetRecordCount - 1
977     else
978     result := 4;
979     end;
980    
981     procedure DsMoveBy(Delta: Integer);
982     begin
983     FDataLink.DataSet.MoveBy(Delta);
984     GetScrollbarParams(aRange, aPage, aPos);
985     end;
986    
987     procedure DsGoto(BOF: boolean);
988     begin
989     if BOF then FDatalink.DataSet.First
990     else FDataLink.DataSet.Last;
991     GetScrollbarParams(aRange, aPage, aPos);
992     end;
993    
994     function DsPos: boolean;
995     begin
996     result := false;
997     aPos := Message.Pos;
998     if aPos=FOldPosition then begin
999     result := true;
1000     exit;
1001     end;
1002     if aPos>=MaxPos then
1003     dsGoto(False)
1004     else if aPos<=0 then
1005     dsGoto(True)
1006     else if IsSeq then
1007     FDatalink.DataSet.RecNo := aPos + 1
1008     else begin
1009     DeltaRec := Message.Pos - FOldPosition;
1010     if DeltaRec=0 then begin
1011     result := true;
1012     exit
1013     end
1014     else if DeltaRec<-1 then
1015     DsMoveBy(-VisibleRowCount)
1016     else if DeltaRec>1 then
1017     DsMoveBy(VisibleRowCount)
1018     else
1019     DsMoveBy(DeltaRec);
1020     end;
1021     end;
1022    
1023     begin
1024     if not FDatalink.Active or not assigned(FDataLink.DataSet) then exit;
1025    
1026     IsSeq := FDatalink.DataSet.IsSequenced and not FDataLink.DataSet.Filtered;
1027     case Message.ScrollCode of
1028     SB_TOP:
1029     DsGoto(True);
1030     SB_BOTTOM:
1031     DsGoto(False);
1032     SB_PAGEUP:
1033     DsMoveBy(-VisibleRowCount);
1034     SB_LINEUP:
1035     DsMoveBy(-1);
1036     SB_LINEDOWN:
1037     DsMoveBy(1);
1038     SB_PAGEDOWN:
1039     DsMoveBy(VisibleRowCount);
1040     SB_THUMBPOSITION:
1041     if DsPos then
1042     exit;
1043     SB_THUMBTRACK:
1044     if not (FDatalink.DataSet.IsSequenced) or DsPos then
1045     begin
1046     exit;
1047     end;
1048     else begin
1049     Exit;
1050     end;
1051     end;
1052    
1053     ScrollBarPosition(SB_VERT, aPos);
1054     FOldPosition:=aPos; end;
1055    
1056     function TDBControlGrid.ISEOF: boolean;
1057     begin
1058     with FDatalink do
1059     result := ValidDataSet and DataSet.EOF;
1060     end;
1061    
1062     function TDBControlGrid.ValidDataSet: boolean;
1063     begin
1064     result := FDatalink.Active And (FDatalink.DataSet<>nil)
1065     end;
1066    
1067     function TDBControlGrid.InsertCancelable: boolean;
1068     begin
1069     Result := ValidDataSet;
1070     if Result then
1071     with FDatalink.DataSet do
1072     Result := (State=dsInsert) and not Modified ;
1073     end;
1074    
1075     function TDBControlGrid.GetBufferCount: integer;
1076     begin
1077     Result := ClientHeight div DefaultRowHeight;
1078     end;
1079    
1080     procedure TDBControlGrid.DoEnter;
1081     begin
1082     inherited DoEnter;
1083     FWeHaveFocus := true;
1084     end;
1085    
1086     procedure TDBControlGrid.DoExit;
1087     begin
1088     FWeHaveFocus := false;
1089     if ValidDataSet and (dgpCancelOnExit in Options) and
1090     InsertCancelable then
1091     begin
1092     FDataLink.DataSet.Cancel;
1093     end;
1094     inherited DoExit;
1095     end;
1096    
1097     procedure TDBControlGrid.DoGridResize;
1098     begin
1099     if Columns.Count = 0 then Exit;
1100    
1101     if ColCount > 1 then
1102     Columns[0].Width := ClientWidth - ColWidths[0]
1103     else
1104     Columns[0].Width := ClientWidth;
1105    
1106     FRowCache.Width := Columns[0].Width;
1107     UpdateDrawPanelBounds(Row);
1108     end;
1109    
1110     procedure TDBControlGrid.DoOnResize;
1111     begin
1112     inherited DoOnResize;
1113     DoGridResize;
1114     end;
1115    
1116     procedure TDBControlGrid.DoScrollDataSet(Data: PtrInt);
1117     begin
1118     if AppDestroying in Application.Flags then Exit;
1119     FDataLink.DataSet.MoveBy(integer(Data) - FDataLink.DataSet.RecNo);
1120     end;
1121    
1122 tony 27 procedure TDBControlGrid.DoSelectNext(Data: PtrInt);
1123     begin
1124     FDataLink.DataSet.MoveBy(1);
1125     end;
1126    
1127 tony 23 procedure TDBControlGrid.DrawAllRows;
1128     begin
1129     inherited DrawAllRows;
1130     if ValidDataSet and FDatalink.DataSet.Active then
1131     begin
1132     if FInCacheRefresh and not FCacheRefreshQueued then
1133     {We are at the end of a cache refresh cycle}
1134     begin
1135     if FRequiredRecNo > 0 then
1136     begin
1137     if FRequiredRecNo <> FDataLink.DataSet.RecNo then
1138     Application.QueueAsyncCall(@DoScrollDataSet,FRequiredRecNo);
1139     FRequiredRecNo := 0;
1140     end
1141     else
1142     if FDrawRow <> FSelectedRow then
1143     Application.QueueAsyncCall(@DoMoveRecord,FSelectedRow);
1144     end;
1145     FInCacheRefresh := false;
1146     end;
1147     end;
1148    
1149     procedure TDBControlGrid.DrawRow(ARow: Integer);
1150     begin
1151     if (ARow>=FixedRows) and FDataLink.Active then
1152     FDrawingActiveRecord := (ARow = FDrawRow)
1153     else
1154     FDrawingActiveRecord := False;
1155     inherited DrawRow(ARow);
1156     end;
1157    
1158     procedure TDBControlGrid.DrawCell(aCol, aRow: Integer; aRect: TRect;
1159     aState: TGridDrawState);
1160    
1161     function GetDatasetState: TDataSetState;
1162     begin
1163     if ValidDataSet then
1164     result := FDataLink.DataSet.State
1165     else
1166     result := dsInactive;
1167     end;
1168    
1169     var
1170     DataCol: Integer;
1171     begin
1172     PrepareCanvas(aCol, aRow, aState);
1173    
1174     if aCol < FixedCols then
1175     DrawIndicator(Canvas,aRow, aRect,GetDataSetState,false)
1176     else
1177 tony 29 if FDrawPanel = nil then
1178     DrawFillRect(Canvas,aRect) else
1179 tony 23 if not FDrawingActiveRecord and FDataLink.Active then
1180     DoDrawRow(aRow,aRect,aState);
1181     {if we are drawing the active record then this is rendered by the Draw Panel
1182     i.e. a child control - so we need do nothing here}
1183    
1184     DrawCellGrid(aCol, aRow, aRect, aState);
1185     end;
1186    
1187     procedure TDBControlGrid.DrawIndicator(ACanvas: TCanvas; aRow: integer;
1188     R: TRect; Opt: TDataSetState; MultiSel: boolean);
1189     var
1190     dx,dy, x, y: Integer;
1191     procedure CenterY;
1192     begin
1193     y := R.Top + (R.Bottom-R.Top) div 2;
1194     end;
1195     procedure CenterX;
1196     begin
1197     X := R.Left + (R.Right-R.Left) div 2;
1198     end;
1199     procedure DrawEdit(clr: Tcolor);
1200     begin
1201     ACanvas.Pen.Color := clr;
1202     CenterY;
1203     CenterX;
1204     ACanvas.MoveTo(X-2, Y-Dy);
1205     ACanvas.LineTo(X+3, Y-Dy);
1206     ACanvas.MoveTo(X, Y-Dy);
1207     ACanvas.LineTo(X, Y+Dy);
1208     ACanvas.MoveTo(X-2, Y+Dy);
1209     ACanvas.LineTo(X+3, Y+Dy);
1210     end;
1211     procedure DrawBrowse;
1212     begin
1213     ACanvas.Brush.Color:=clBlack;
1214     ACanvas.Pen.Color:=clBlack;
1215     CenterY;
1216     x:= R.Left+3;
1217     if MultiSel then begin
1218     if BiDiMode = bdRightToLeft then begin
1219     ACanvas.Polyline([point(x+dx,y-dy), point(x,y),point(x+dx,y+dy), point(x+dx,y+dy-1)]);
1220     ACanvas.Polyline([point(x+dx,y-dy+1), point(x+1,y),point(x+dx,y+dy-1), point(x+dx,y+dy-2)]);
1221     CenterX;
1222     Dec(X,3);
1223     ACanvas.Ellipse(Rect(X+dx-2,Y-2,X+dx+2,Y+2));
1224     end else begin
1225     ACanvas.Polyline([point(x,y-dy), point(x+dx,y),point(x,y+dy), point(x,y+dy-1)]);
1226     ACanvas.Polyline([point(x,y-dy+1),point(x+dx-1,y),point(x, y+dy-1), point(x,y+dy-2)]);
1227     CenterX;
1228     Dec(X,3);
1229     ACanvas.Ellipse(Rect(X-2,Y-2,X+2,Y+2));
1230     end;
1231     end else begin
1232     if BiDiMode = bdRightToLeft then
1233     ACanvas.Polygon([point(x,y),point(x+dx,y-dy),point(x+dx, y+dy),point(x,y)])
1234     else
1235     ACanvas.Polygon([point(x,y-dy),point(x+dx,y),point(x, y+dy),point(x,y-dy)]);
1236     end;
1237     end;
1238    
1239     begin
1240     ACanvas.Brush.Color := FixedColor;
1241     ACanvas.FillRect(R);
1242     if aRow <> Row then Exit;
1243    
1244     dx := 6;
1245     dy := 6;
1246     case Opt of
1247     dsBrowse:
1248     DrawBrowse;
1249     dsEdit:
1250     if FDrawingActiveRecord then
1251     DrawEdit(clBlack)
1252     else
1253     DrawBrowse;
1254     dsInsert:
1255     if FDrawingActiveRecord then
1256     DrawEdit(clGreen)
1257     else
1258     DrawBrowse;
1259     else
1260     if MultiSel then begin
1261     ACanvas.Brush.Color:=clBlack;
1262     ACanvas.Pen.Color:=clBlack;
1263     CenterX;
1264     CenterY;
1265     ACanvas.Ellipse(Rect(X-3,Y-3,X+3,Y+3));
1266     end;
1267     end; end;
1268    
1269     procedure TDBControlGrid.GridMouseWheel(shift: TShiftState; Delta: Integer);
1270     begin
1271     inherited GridMouseWheel(shift, Delta);
1272     self.SetFocus;
1273     if ValidDataSet then
1274     FDataLink.DataSet.MoveBy(Delta);
1275     end;
1276    
1277     procedure TDBControlGrid.KeyDown(var Key: Word; Shift: TShiftState);
1278     type
1279     TOperation=(opMoveBy,opCancel,opAppend,opInsert,opDelete);
1280     var
1281     DeltaCol,DeltaRow: Integer;
1282    
1283     procedure DoOnKeyDown;
1284     begin
1285     if Assigned(OnKeyDown) then
1286     OnKeyDown(Self, Key, Shift);
1287     end;
1288    
1289     procedure DoOperation(AOper: TOperation; Arg: Integer = 0);
1290     begin
1291     self.SetFocus;
1292     case AOper of
1293     opMoveBy:
1294     FDatalink.DataSet.MoveBy(Arg);
1295     opCancel:
1296     begin
1297     FDatalink.Dataset.Cancel;
1298     end;
1299     opAppend:
1300     FDatalink.Dataset.Append;
1301     opInsert:
1302     FDatalink.Dataset.Insert;
1303     opDelete:
1304     FDatalink.Dataset.Delete;
1305     end;
1306     end;
1307    
1308     function doVKDown: boolean;
1309     begin
1310     if InsertCancelable then
1311     begin
1312     if IsEOF then
1313     result:=true
1314     else begin
1315     doOperation(opCancel);
1316     result := false;
1317     end;
1318     end else begin
1319     result:=false;
1320     doOperation(opMoveBy, 1);
1321     if GridCanModify and FDataLink.EOF then begin
1322     if not (dgpDisableInsert in Options) then
1323     doOperation(opAppend);
1324     end
1325     end;
1326     end;
1327    
1328     function DoVKUP: boolean;
1329     begin
1330     if InsertCancelable then
1331     doOperation(opCancel)
1332     else begin
1333     doOperation(opMoveBy, -1);
1334     end;
1335     result := FDatalink.DataSet.BOF;
1336     end;
1337    
1338    
1339    
1340     begin
1341     case Key of
1342     VK_DOWN:
1343     begin
1344     DoOnKeyDown;
1345     if (Key<>0) and ValidDataset then begin
1346     doVKDown;
1347     Key := 0;
1348     end;
1349     end;
1350    
1351     VK_UP:
1352     begin
1353     doOnKeyDown;
1354     if (Key<>0) and ValidDataset then begin
1355     doVKUp;
1356     key := 0;
1357     end;
1358     end;
1359    
1360     VK_NEXT:
1361     begin
1362     doOnKeyDown;
1363     if (Key<>0) and ValidDataset then begin
1364     doOperation(opMoveBy, VisibleRowCount);
1365     Key := 0;
1366     end;
1367     end;
1368    
1369     VK_PRIOR:
1370     begin
1371     doOnKeyDown;
1372     if (Key<>0) and ValidDataset then begin
1373     doOperation(opMoveBy, -VisibleRowCount);
1374     key := 0;
1375     end;
1376     end;
1377    
1378     VK_ESCAPE:
1379     begin
1380     doOnKeyDown;
1381     if ValidDataSet then
1382     doOperation(opCancel);
1383     end;
1384    
1385     VK_HOME:
1386     begin
1387     doOnKeyDown;
1388     if (Key<>0) and ValidDataSet then
1389     begin
1390     if ssCTRL in Shift then
1391     begin
1392     FDataLink.DataSet.First;
1393     Key:=0;
1394     end;
1395     end;
1396     end;
1397    
1398     VK_END:
1399     begin
1400     doOnKeyDown;
1401     if Key<>0 then begin
1402     if ValidDataSet then
1403     begin
1404     if ssCTRL in shift then
1405     begin
1406     FDatalink.DataSet.Last;
1407     Key:=0;
1408     end;
1409     end;
1410     end;
1411     end;
1412    
1413     end;
1414     end;
1415    
1416     procedure TDBControlGrid.LinkActive(Value: Boolean);
1417     begin
1418     if not Value then
1419     begin
1420     FRowCache.ClearCache;
1421     FInCacheRefresh := false;
1422     FCacheRefreshQueued := false;
1423     Row := FixedRows;
1424 tony 35 FDrawingActiveRecord := false;
1425     FSelectedRecNo := 0;
1426     FSelectedRow := 0;
1427     FRequiredRecNo := 0;
1428 tony 23 end;
1429     FRowCache.UseAlternateColors := AlternateColor <> Color;
1430     FRowCache.AltColorStartNormal := AltColorStartNormal;
1431     FLastRecordCount := 0;
1432     LayoutChanged;
1433     if Value then
1434     begin
1435     { The problem being solved here is that TDataSet does not readily tell us
1436     when a record is deleted. We get a DataSetChanged event - but this can
1437     occur for many reasons. Only by monitoring the record count accurately
1438     can be determine when a record is deleted. To do this we need to scroll
1439     the whole dataset to the end when the dataset is activated. Not desirable
1440     with large datasets - but a fix to TDataSet is needed to avoid this.
1441     }
1442     FDataLink.DataSet.DisableControls;
1443     try
1444     FDataLink.DataSet.Last;
1445     FLastRecordCount := FDataLink.DataSet.RecordCount;
1446     if not FDefaultPositionAtEnd then
1447     FDataLink.DataSet.First;
1448     FRequiredRecNo := FDataLink.DataSet.RecNo;
1449     finally
1450     FDataLink.DataSet.EnableControls;
1451     end;
1452     end;
1453     end;
1454    
1455     procedure TDBControlGrid.LayoutChanged;
1456     begin
1457     if csDestroying in ComponentState then
1458     exit;
1459     BeginUpdate;
1460     try
1461     if UpdateGridCounts=0 then
1462     EmptyGrid;
1463     finally
1464     EndUpdate;
1465     end;
1466     UpdateScrollbarRange;
1467     end;
1468    
1469     procedure TDBControlGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
1470     Y: Integer);
1471     var
1472     Gz: TGridZone;
1473     P: TPoint;
1474     procedure doMouseDown;
1475     begin
1476     // if not Focused then
1477     // SetFocus;
1478     if assigned(OnMouseDown) then
1479     OnMouseDown(Self, Button, Shift, X, Y);
1480     end;
1481     procedure doInherited;
1482     begin
1483     inherited MouseDown(Button, Shift, X, Y);
1484     end;
1485     procedure doMoveBy;
1486     begin
1487     FDatalink.DataSet.MoveBy(P.Y - Row);
1488     end;
1489     procedure doMoveToColumn;
1490     begin
1491     Col := P.X;
1492     end;
1493     procedure DoCancel;
1494     begin
1495     FDatalink.Dataset.cancel;
1496     end;
1497     begin
1498     if (csDesigning in componentState) or not ValidDataSet then begin
1499     exit;
1500     end;
1501     self.SetFocus;
1502    
1503     { if not MouseButtonAllowed(Button) then begin
1504     doInherited;
1505     exit;
1506     end;}
1507    
1508     Gz:=MouseToGridZone(X,Y);
1509     CacheMouseDown(X,Y);
1510     case Gz of
1511     gzInvalid:
1512     doMouseDown;
1513    
1514     gzFixedCells, gzFixedCols:
1515     doInherited;
1516     else
1517     begin
1518    
1519     P:=MouseToCell(Point(X,Y));
1520     if Gz=gzFixedRows then
1521     P.X := Col;
1522    
1523     if P.Y=Row then begin
1524     //doAcceptValue;
1525    
1526     if not (ssCtrl in Shift) then
1527     begin
1528     if gz=gzFixedRows then
1529     doMouseDown
1530     else
1531     doInherited;
1532     end;
1533    
1534     end else begin
1535     doMouseDown;
1536     if ValidDataSet then begin
1537     if InsertCancelable and IsEOF then
1538     doCancel;
1539     doMoveBy;
1540     end;
1541     end;
1542     end;
1543     end;
1544     end;
1545    
1546     procedure TDBControlGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
1547     Y: Integer);
1548     begin
1549     inherited MouseUp(Button, Shift, X, Y);
1550     FLastMouse.X := X;
1551     FLastMouse.Y := Y;
1552     FLastMouseButton := Button;
1553     FLastMouseShiftState := Shift;
1554     Application.QueueAsyncCall(@DoSendMouseClicks,0);
1555     end;
1556    
1557     procedure TDBControlGrid.MoveSelection;
1558     begin
1559     inherited MoveSelection;
1560     InvalidateRow(Row);
1561     end;
1562    
1563     procedure TDBControlGrid.Notification(AComponent: TComponent;
1564     Operation: TOperation);
1565     begin
1566     inherited Notification(AComponent, Operation);
1567     if (Operation = opRemove) and
1568     (AComponent = FDrawPanel) then FDrawPanel := nil;
1569     end;
1570    
1571     procedure TDBControlGrid.PrepareCanvas(aCol, aRow: Integer; aState: TGridDrawState
1572     );
1573     begin
1574     inherited PrepareCanvas(aCol, aRow, aState);
1575    
1576     if gdFixed in aState then
1577     begin
1578     if gdHot in aState then
1579     Canvas.Brush.Color := FixedHotColor
1580     else
1581     Canvas.Brush.Color := GetColumnColor(aCol, gdFixed in AState);
1582     end;
1583    
1584     if (not FDatalink.Active) and ((gdSelected in aState) or (gdFocused in aState)) then
1585     Canvas.Brush.Color := Self.Color;
1586    
1587     end;
1588    
1589     procedure TDBControlGrid.ResetSizes;
1590     begin
1591     LayoutChanged;
1592     inherited ResetSizes;
1593     DoGridResize;
1594     end;
1595    
1596     procedure TDBControlGrid.SetColor(Value: TColor);
1597     begin
1598     inherited SetColor(Value);
1599     if (csDesigning in ComponentState) and assigned(FDrawPaneL) then
1600     FDrawPanel.Color := Value;
1601     end;
1602    
1603     procedure TDBControlGrid.UpdateActive;
1604     var
1605     PrevRow: Integer;
1606     begin
1607     if (csDestroying in ComponentState) or
1608     (FDatalink=nil) or (not FDatalink.Active) or
1609     (FDatalink.ActiveRecord<0) then
1610     exit;
1611    
1612     FDrawRow := FixedRows + FDataLink.ActiveRecord;
1613     FSelectedRecNo := FDataLink.DataSet.RecNo;
1614     PrevRow := Row;
1615     Row := FDrawRow;
1616     if not FInCacheRefresh then
1617 tony 29 begin
1618 tony 23 FSelectedRow := FDrawRow;
1619 tony 29 if FDatalink.DataSet.State <> dsInsert then
1620     FRowCache.InvalidateRowImage(FSelectedRecNo);
1621     end;
1622 tony 23 InvalidateRow(PrevRow);
1623     SetupDrawPanel(FDrawRow);
1624     end;
1625    
1626     procedure TDBControlGrid.UpdateData;
1627     begin
1628     FModified := false;
1629     end;
1630    
1631     procedure TDBControlGrid.UpdateShowing;
1632     begin
1633     inherited UpdateShowing;
1634     DoGridResize
1635     end;
1636    
1637     procedure TDBControlGrid.UpdateVertScrollbar(const aVisible: boolean;
1638     const aRange, aPage, aPos: Integer);
1639     begin
1640     UpdateScrollbarRange;
1641     end;
1642    
1643     constructor TDBControlGrid.Create(AOwner: TComponent);
1644     begin
1645     inherited Create(AOwner);
1646     FDataLink := TDBControlGridDataLink.Create;//(Self);
1647     FRowCache := TRowCache.Create;
1648     FDataLink.OnRecordChanged:=@OnRecordChanged;
1649     FDataLink.OnDatasetChanged:=@OnDataSetChanged;
1650     FDataLink.OnDataSetOpen:=@OnDataSetOpen;
1651     FDataLink.OnDataSetClose:=@OnDataSetClose;
1652     FDataLink.OnNewDataSet:=@OnNewDataSet;
1653     FDataLink.OnInvalidDataSet:=@OnInvalidDataset;
1654     FDataLink.OnInvalidDataSource:=@OnInvalidDataSource;
1655     FDataLink.OnDataSetScrolled:=@OnDataSetScrolled;
1656     FDataLink.OnLayoutChanged:=@OnLayoutChanged;
1657     FDataLink.OnEditingChanged:=@OnEditingChanged;
1658     FDataLink.OnUpdateData:=@OnUpdateData;
1659     FDataLink.OnCheckBrowseMode := @OnCheckBrowseMode;
1660     FDataLink.VisualControl:= True;
1661     ScrollBars := ssAutoVertical;
1662     FOptions := [dgpIndicator];
1663     FixedCols := 1;
1664     ColCount := 1;
1665     FixedRows := 0;
1666     RowCount := 1;
1667     ColWidths[0] := 12;
1668     Columns.Add.ReadOnly := true; {Add Dummy Column for Panel}
1669     DoGridResize;
1670     if not (csDesigning in ComponentState) then
1671     Application.AddOnKeyDownBeforeHandler(@KeyDownHandler,false);
1672     end;
1673    
1674     destructor TDBControlGrid.Destroy;
1675     begin
1676     if assigned(FDataLink) then
1677     begin
1678     FDataLink.OnDataSetChanged:=nil;
1679     FDataLink.OnRecordChanged:=nil;
1680     FDataLink.Free;
1681     end;
1682     if assigned(FRowCache) then FRowCache.Free;
1683     inherited Destroy;
1684     end;
1685    
1686     function TDBControlGrid.MouseToRecordOffset(const x, y: Integer;
1687     out RecordOffset: Integer): TGridZone;
1688     var
1689     aCol,aRow: Integer;
1690     begin
1691     Result := MouseToGridZone(x, y);
1692    
1693     RecordOffset := 0;
1694    
1695     if (Result=gzInvalid) or (Result=gzFixedCells) then
1696     exit;
1697    
1698     MouseToCell(x, y, aCol, aRow);
1699    
1700     if (Result=gzFixedRows) or (Result=gzNormal) then
1701     RecordOffset := aRow - Row;
1702    
1703     if (Result=gzFixedCols) or (Result=gzNormal) then begin
1704     aRow := ColumnIndexFromGridColumn(aCol);
1705     end;
1706     end;
1707    
1708     function TDBControlGrid.ExecuteAction(AAction: TBasicAction): Boolean;
1709     begin
1710     Result := (DataLink <> nil)
1711     and DataLink.ExecuteAction(AAction);
1712     end;
1713    
1714     function TDBControlGrid.UpdateAction(AAction: TBasicAction): Boolean;
1715     begin
1716     Result := (DataLink <> nil)
1717     and DataLink.UpdateAction(AAction);
1718     end;
1719    
1720     end.