ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/DBControlGrid.pas
Revision: 315
Committed: Thu Feb 25 11:56:36 2021 UTC (3 years, 1 month ago) by tony
Content type: text/x-pascal
File size: 46748 byte(s)
Log Message:
Updated for IBX 4 release

File Contents

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