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 (2 years, 4 months ago) by tony
Content type: text/x-pascal
File size: 46813 byte(s)
Log Message:
initiate test 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 Container.Canvas.FillRect(0,0,Control.Width,Control.Height);
312 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 FList[i].FBitmap := nil;
374 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 SetLength(FList,0);
422 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 if FList[RecNo].FBitmap <> nil then
430 FList[RecNo].FBitmap.Free;
431 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 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 FList[RecNo].FBitmap := nil;
488 FList[RecNo].FState := rcEmpty;
489 end;
490 end;
491
492 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 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 procedure TDBControlGrid.EmptyGrid;
546 var
547 OldFixedRows: Integer;
548 begin
549 OldFixedRows := FixedRows;
550 Clear;
551 FRowCache.ClearCache;
552 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 end;
617 Canvas.FillRect(aRect);
618 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 FDatalink.DataSet.MoveBy(aRow - FDrawRow);
633 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 AControl: TControl;
669 begin
670 if Visible and assigned(FDrawPanel) and FDrawPanel.Visible and FWeHaveFocus
671 and (Self.Owner=Screen.ActiveForm) then
672 begin
673 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 and (Key in [VK_RETURN,VK_UP,VK_DOWN]) then Exit; {Ignore Return in a CustomMemo}
682
683 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 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 FRowCache.Add2Cache(RecNo,FDrawPanel);
713 end;
714 end;
715
716 procedure TDBControlGrid.OnDataSetChanged(aDataSet: TDataSet);
717 begin
718 if aDataSet.State = dsBrowse then
719 begin
720 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 LayoutChanged;
734 end;
735 FLastRecordCount := GetRecordCount;
736 if aDataSet.State = dsInsert then
737 begin
738 FRequiredRecNo := aDataSet.RecNo + 1;
739 Application.QueueAsyncCall(@DoSelectNext,0);
740 end;
741 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 while not ((theForm is TCustomForm) or (theForm is TCustomFrame))
830 and (theForm.Parent <> nil) do
831 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 FDrawPanel.AddHandlerOnResize(@OnDrawPanelResize);
848 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 if FDrawPanel = nil then Exit;
872 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 procedure TDBControlGrid.DoSelectNext(Data: PtrInt);
1137 begin
1138 FDataLink.DataSet.MoveBy(1);
1139 end;
1140
1141 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 if (FDrawPanel = nil) or not FDataLink.Active then
1190 DrawFillRect(Canvas,aRect)
1191 else
1192 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 FDrawingActiveRecord := false;
1436 FSelectedRecNo := 0;
1437 FSelectedRow := 0;
1438 FRequiredRecNo := 0;
1439 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 begin
1629 FSelectedRow := FDrawRow;
1630 if FDatalink.DataSet.State <> dsInsert then
1631 FRowCache.InvalidateRowImage(FSelectedRecNo);
1632 end;
1633 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 if not (csDesigning in ComponentState) then
1695 Application.RemoveOnKeyDownBeforeHandler( @KeyDownHandler );
1696 Application.RemoveAsyncCalls(self);
1697 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