ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/ibcontrols/DBControlGrid.pas
Revision: 362
Committed: Tue Dec 7 13:27:39 2021 UTC (3 years ago) by tony
Content type: text/x-pascal
File size: 46813 byte(s)
Log Message:
initiate test release

File Contents

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