ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/DBControlGrid.pas
Revision: 143
Committed: Fri Feb 23 12:11:21 2018 UTC (6 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 46582 byte(s)
Log Message:
Fixes Merged

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 TRowCacheState = (rcEmpty,rcPresent,rcDeleted);
72 TRowDetails = record
73 FState: TRowCacheState;
74 FAlternateColor: boolean;
75 FBitmap: TBitmap;
76 end;
77
78 { TRowCache }
79
80 TRowCache = class
81 private
82 FAltColorStartNormal: boolean;
83 FHeight: integer;
84 FList: array of TRowDetails;
85 FUseAlternateColors: boolean;
86 FWidth: integer;
87 procedure FreeImages(Reset: boolean);
88 function GetAlternateColor(RecNo: integer): boolean;
89 function Render(Control: TWinControl): TBitmap;
90 procedure ExtendCache(aMaxIndex: integer);
91 procedure OnWidthChange(Sender: TObject);
92 procedure SetHeight(AValue: integer);
93 procedure SetUseAlternateColors(AValue: boolean);
94 procedure SetWidth(AValue: integer);
95 public
96 constructor Create;
97 destructor Destroy; override;
98 procedure ClearCache;
99 function Add2Cache(RecNo: Longint; Control: TWinControl): TBitmap;
100 function GetRowImage(RecNo, Offset: integer): TBitmap;
101 procedure InvalidateRowImage(RecNo: integer);
102 function IsEmpty(RecNo: integer): boolean;
103 procedure MarkAsDeleted(RecNo: integer);
104 property AlternateColor[RecNo: integer]: boolean read GetAlternateColor;
105 property Width: integer read FWidth write SetWidth;
106 property Height: integer read FHeight write SetHeight;
107 property AltColorStartNormal: boolean read FAltColorStartNormal write FAltColorStartNormal;
108 property UseAlternateColors: boolean read FUseAlternateColors write SetUseAlternateColors;
109 end;
110
111 { TDBControlGridDataLink }
112
113 TDBControlGridDataLink = class(TComponentDataLink)
114 private
115 FOnCheckBrowseMode: TDataSetNotifyEvent;
116 protected
117 procedure CheckBrowseMode; override;
118 public
119 property OnCheckBrowseMode: TDataSetNotifyEvent read FOnCheckBrowseMode write FOnCheckBrowseMode;
120 end;
121
122 TKeyDownHandler = procedure (Sender: TObject; var Key: Word; Shift: TShiftState; var Done: boolean) of object;
123
124 TPanelGridOption = (dgpIndicator,dgpDisableInsert,dgpCancelOnExit);
125 TPanelGridOptions = set of TPanelGridOption;
126
127 { TDBControlGrid }
128
129 TDBControlGrid = class(TCustomGrid)
130 private
131 { Private declarations }
132 FDataLink: TDBControlGridDataLink;
133 FDefaultPositionAtEnd: boolean;
134 FDrawPanel: TWinControl;
135 FDrawingActiveRecord: boolean;
136 FOldPosition: Integer;
137 FOnKeyDownHander: TKeyDownHandler;
138 FOptions: TPanelGridOptions;
139 FWeHaveFocus: boolean;
140 FRowCache: TRowCache;
141 FDrawRow: integer; {The current row in the draw panel}
142 FSelectedRow: integer; {The row containing the current selection}
143 FSelectedRecNo: integer; {The DataSet RecNo for the current row}
144 FRequiredRecNo: integer; {Used after a big jump and is the dataset recno
145 that we want to end up with}
146 FInCacheRefresh: boolean; {Cache refresh in progress during paint}
147 FCacheRefreshQueued: boolean; {cache refresh requested during wmpaint}
148 FModified: boolean;
149 FLastRecordCount: integer;
150
151 {Used to pass mouse clicks to panel when focused row changes}
152 FLastMouse: TPoint;
153 FLastMouseButton: TMouseButton;
154 FLastMouseShiftState: TShiftState;
155
156 function ActiveControl: TControl;
157 procedure EmptyGrid;
158 function GetDataSource: TDataSource;
159 function GetRecordCount: Integer;
160 procedure GetScrollbarParams(out aRange, aPage, aPos: Integer);
161 function GridCanModify: boolean;
162 procedure DoDrawRow(aRow: integer; aRect: TRect; aState: TGridDrawState);
163 procedure DoMoveRecord(Data: PtrInt);
164 procedure DoSelectNext(Data: PtrInt);
165 procedure DoScrollDataSet(Data: PtrInt);
166 procedure DoSetupDrawPanel(Data: PtrInt);
167 procedure DoSendMouseClicks(Data: PtrInt);
168 procedure KeyDownHandler(Sender: TObject; var Key: Word; Shift: TShiftState);
169 procedure OnRecordChanged(Field:TField);
170 procedure OnCheckBrowseMode(aDataSet: TDataSet);
171 procedure OnDataSetChanged(aDataSet: TDataSet);
172 procedure OnDataSetOpen(aDataSet: TDataSet);
173 procedure OnDataSetClose(aDataSet: TDataSet);
174 procedure OnDrawPanelResize(Sender: TObject);
175 procedure OnEditingChanged(aDataSet: TDataSet);
176 procedure OnInvalidDataSet(aDataSet: TDataSet);
177 procedure OnInvalidDataSource(aDataSet: TDataset);
178 procedure OnLayoutChanged(aDataSet: TDataSet);
179 procedure OnNewDataSet(aDataSet: TDataset);
180 procedure OnDataSetScrolled(aDataSet:TDataSet; Distance: Integer);
181 procedure OnUpdateData(aDataSet: TDataSet);
182 procedure SetDataSource(AValue: TDataSource);
183 procedure SetDrawPanel(AValue: TWinControl);
184 procedure SetOptions(AValue: TPanelGridOptions);
185 procedure SetupDrawPanel(aRow: integer);
186 function UpdateGridCounts: Integer;
187 procedure UpdateBufferCount;
188 procedure UpdateDrawPanelBounds(aRow: integer);
189 procedure UpdateScrollbarRange;
190 procedure WMVScroll(var Message : TLMVScroll); message LM_VScroll;
191 function ISEOF: boolean;
192 function ValidDataSet: boolean;
193 function InsertCancelable: boolean;
194 protected
195 { Protected declarations }
196 function GetBufferCount: integer; virtual;
197 procedure DoEnter; override;
198 procedure DoExit; override;
199 procedure DoGridResize;
200 procedure DoOnResize; override;
201 procedure DrawAllRows; override;
202 procedure DrawRow(ARow: Integer); override;
203 procedure DrawCell(aCol,aRow: Integer; aRect: TRect; aState:TGridDrawState); override;
204 procedure DrawIndicator(ACanvas: TCanvas; aRow: integer; R: TRect; Opt: TDataSetState; MultiSel: boolean); virtual;
205 procedure GridMouseWheel(shift: TShiftState; Delta: Integer); override;
206 procedure KeyDown(var Key : Word; Shift : TShiftState); override;
207 procedure LinkActive(Value: Boolean); virtual;
208 procedure LayoutChanged; virtual;
209 procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
210 procedure MouseUp(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
211 procedure MoveSelection; override;
212 procedure Notification(AComponent: TComponent; Operation: TOperation); override;
213 procedure PrepareCanvas(aCol,aRow: Integer; aState:TGridDrawState); override;
214 procedure ResetSizes; override;
215 procedure SetColor(Value: TColor); override;
216 procedure UpdateActive; virtual;
217 procedure UpdateData; virtual;
218 procedure UpdateShowing; override;
219 procedure UpdateVertScrollbar(const aVisible: boolean; const aRange,aPage,aPos: Integer); override;
220 public
221 { Public declarations }
222 constructor Create(AOwner: TComponent); override;
223 destructor Destroy; override;
224 function MouseToRecordOffset(const x, y: Integer; out RecordOffset: Integer
225 ): TGridZone;
226 function ExecuteAction(AAction: TBasicAction): Boolean; override;
227 function UpdateAction(AAction: TBasicAction): Boolean; override;
228 property Datalink: TDBControlGridDataLink read FDatalink;
229 published
230 { Published declarations }
231 property Align;
232 property AlternateColor;
233 property AltColorStartNormal;
234 property Anchors;
235 property BiDiMode;
236 property BorderSpacing;
237 property BorderStyle;
238 property CellHintPriority;
239 property Color;
240 property Constraints;
241 property DataSource: TDataSource read GetDataSource write SetDataSource;
242 property DefaultPositionAtEnd: boolean read FDefaultPositionAtEnd write FDefaultPositionAtEnd;
243 property DragCursor;
244 property DragMode;
245 property DrawPanel: TWinControl read FDrawPanel write SetDrawPanel;
246 property Enabled;
247 property FixedColor;
248 property FixedCols;
249 property Flat;
250 property Font;
251 property Options: TPanelGridOptions read FOptions write SetOptions;
252 property ParentBiDiMode;
253 property ParentColor default false;
254 property ParentFont;
255 property PopupMenu;
256 property Scrollbars default ssAutoVertical;
257 property ShowHint;
258 property TabOrder;
259 property TabStop;
260 property UseXORFeatures;
261 property Visible;
262 property OnContextPopup;
263 property OnDblClick;
264 property OnDragDrop;
265 property OnDragOver;
266 property OnEndDrag;
267 property OnEnter;
268 property OnExit;
269 property OnGetCellHint;
270 property OnKeyDown;
271 property OnKeyPress;
272 property OnKeyUp;
273 property OnKeyDownHander: TKeyDownHandler read FOnKeyDownHander write FOnKeyDownHander;
274 property OnMouseDown;
275 property OnMouseEnter;
276 property OnMouseLeave;
277 property OnMouseMove;
278 property OnMouseUp;
279 property OnMouseWheel;
280 property OnMouseWheelDown;
281 property OnMouseWheelUp;
282 property OnPrepareCanvas;
283 property OnStartDrag;
284 property OnUTF8KeyPress;
285 end;
286
287 implementation
288
289 uses LCLType, Math, LCLIntf, Forms, LCLMessageGlue, EditBtn, MaskEdit;
290
291 { TDBControlGridDataLink }
292
293 procedure TDBControlGridDataLink.CheckBrowseMode;
294 begin
295 inherited CheckBrowseMode;
296 if assigned(FOnCheckBrowseMode) then
297 OnCheckBrowseMode(DataSet);
298 end;
299
300 { TRowCache }
301
302 function TRowCache.Render(Control: TWinControl): TBitmap;
303 var Container: TBitmap;
304 begin
305 Container := TBitmap.Create;
306 try
307 Container.SetSize(Control.Width,Control.Height);
308 Control.PaintTo(Container.Canvas,0,0);
309 except
310 Container.Free;
311 raise
312 end;
313 Result := Container;
314 end;
315
316 procedure TRowCache.FreeImages(Reset: boolean);
317 var i: integer;
318 altColor: boolean;
319 begin
320 altColor := not AltColorStartNormal;
321 for i := 0 to Length(FList) - 1 do
322 begin
323 if (FList[i].FState <> rcEmpty) and (FList[i].FBitmap <> nil) then
324 begin
325 FList[i].FBitmap.Free;
326 FList[i].FBitmap := nil;
327 end;
328 if Reset or (FList[i].FState = rcPresent) then
329 FList[i].FState := rcEmpty;
330 if FList[i].FState <> rcDeleted then
331 begin
332 FList[i].FAlternateColor := altColor;
333 altColor := not altColor;
334 end;
335 end;
336 end;
337
338 function TRowCache.GetAlternateColor(RecNo: integer): boolean;
339 begin
340 ExtendCache(RecNo);
341 Dec(RecNo);
342 if (RecNo >= 0) and (RecNo < Length(FList)) then
343 Result := FList[RecNo].FAlternateColor
344 else
345 Result := false;
346 end;
347
348 procedure TRowCache.ExtendCache(aMaxIndex: integer);
349 var i: integer;
350 StartIndex: integer;
351 altColor: boolean;
352 begin
353 if aMaxIndex > Length(FList) then
354 begin
355 aMaxIndex := aMaxIndex + 10;
356 StartIndex := Length(FList);
357 SetLength(FList,aMaxIndex);
358 if not UseAlternateColors then
359 altColor := false
360 else
361 if StartIndex = 0 then
362 altColor := not AltColorStartNormal
363 else
364 altColor := not FList[StartIndex-1].FAlternateColor;
365
366 for i := StartIndex to Length(FList) - 1 do
367 begin
368 FList[i].FState := rcEmpty;
369 FList[i].FBitmap := nil;
370 FList[i].FAlternateColor := altColor;
371 if UseAlternateColors then
372 altColor := not altColor;
373 end;
374 end;
375 end;
376
377 procedure TRowCache.OnWidthChange(Sender: TObject);
378 begin
379 FreeImages(false);
380 end;
381
382 procedure TRowCache.SetHeight(AValue: integer);
383 begin
384 if FHeight = AValue then Exit;
385 FHeight := AValue;
386 FreeImages(false);
387 end;
388
389 procedure TRowCache.SetUseAlternateColors(AValue: boolean);
390 begin
391 if FUseAlternateColors = AValue then Exit;
392 FUseAlternateColors := AValue;
393 FreeImages(false);
394 end;
395
396 procedure TRowCache.SetWidth(AValue: integer);
397 begin
398 if FWidth = AValue then Exit;
399 FWidth := AValue;
400 FreeImages(false);
401 end;
402
403 constructor TRowCache.Create;
404 begin
405 SetLength(FList,0);
406 end;
407
408 destructor TRowCache.Destroy;
409 begin
410 ClearCache;
411 inherited Destroy;
412 end;
413
414 procedure TRowCache.ClearCache;
415 begin
416 FreeImages(true);
417 SetLength(FList,0);
418 end;
419
420 function TRowCache.Add2Cache(RecNo: Longint; Control: TWinControl): TBitmap;
421 var i: integer;
422 begin
423 Dec(RecNo); {Adust to zero base}
424 ExtendCache(RecNo + 1);
425 FList[RecNo].FState := rcPresent;
426 if FList[RecNo].FBitmap <> nil then
427 FList[RecNo].FBitmap.Free;
428 FList[RecNo].FBitmap := Render(Control);
429 Result := FList[RecNo].FBitmap;
430 end;
431
432 function TRowCache.GetRowImage(RecNo, Offset: integer): TBitmap;
433 begin
434 Result := nil;
435 Dec(RecNo); {adjust to zero base}
436 if (RecNo < 0) or (RecNo >= Length(FList)) then
437 Exit;
438
439 if Offset >= 0 then
440 repeat
441 while (RecNo < Length(FList)) and (FList[RecNo].FState = rcDeleted) do
442 Inc(RecNo);
443
444 if RecNo >= Length(FList) then
445 Exit;
446
447 if Offset = 0 then
448 begin
449 if FList[RecNo].FState = rcPresent then
450 Result := FList[RecNo].FBitmap;
451 Exit;
452 end;
453 Inc(RecNo);
454 Dec(Offset);
455 until false
456 else
457 repeat
458 Inc(Offset);
459 Dec(RecNo);
460 while (RecNo > 0) and (FList[RecNo].FState = rcDeleted) do
461 Dec(RecNo);
462
463 if RecNo < 0 then
464 Exit;
465
466 if Offset = 0 then
467 begin
468 if FList[RecNo].FState = rcPresent then
469 Result := FList[RecNo].FBitmap;
470 Exit;
471 end;
472 until false;
473 end;
474
475 procedure TRowCache.InvalidateRowImage(RecNo: integer);
476 begin
477 Dec(RecNo); {adjust to zero base}
478 if (RecNo < 0) or (RecNo >= Length(FList)) then
479 Exit;
480
481 if FList[RecNo].FState = rcPresent then
482 begin
483 FList[RecNo].FBitmap.Free;
484 FList[RecNo].FBitmap := nil;
485 FList[RecNo].FState := rcEmpty;
486 end;
487 end;
488
489 function TRowCache.IsEmpty(RecNo: integer): boolean;
490 begin
491 Dec(RecNo);
492 Result := (RecNo < 0) or (RecNo >= Length(FList)) or (FList[RecNo].FState = rcEmpty);
493 end;
494
495 procedure TRowCache.MarkAsDeleted(RecNo: integer);
496 var altColor: boolean;
497 i: integer;
498 begin
499 Dec(RecNo); {adjust to zero base}
500 if (RecNo < 0) or (RecNo >= Length(FList)) then
501 Exit;
502
503 FList[RecNo].FState := rcDeleted;
504 if not UseAlternateColors then
505 Exit;
506
507 {Reset Alternate Colours}
508
509 if RecNo = 0 then
510 altColor := not AltColorStartNormal
511 else
512 altColor := not FList[RecNo-1].FAlternateColor;
513
514 for i := RecNo + 1 to Length(FList) - 1 do
515 begin
516 if FList[i].FState <> rcDeleted then
517 begin
518 FList[i].FAlternateColor := altColor;
519 altColor := not altColor;
520 if FList[i].FState = rcPresent then
521 begin
522 FList[i].FBitmap.Free;
523 FList[i].FState := rcEmpty;
524 end;
525 end;
526 end;
527 end;
528
529 { TDBControlGrid }
530
531 function TDBControlGrid.ActiveControl: TControl;
532 var AParent: TWinControl;
533 begin
534 Result := nil;
535 AParent := Parent;
536 while (AParent <> nil) and not (AParent is TCustomForm) do
537 AParent := AParent.Parent;
538 if (AParent <> nil) and (AParent is TCustomForm)then
539 Result := TCustomForm(AParent).ActiveControl;
540 end;
541
542 procedure TDBControlGrid.EmptyGrid;
543 var
544 OldFixedRows: Integer;
545 begin
546 OldFixedRows := FixedRows;
547 Clear;
548 FRowCache.ClearCache;
549 RowCount := OldFixedRows + 1;
550 if dgpIndicator in FOptions then
551 ColWidths[0]:=12;
552 if assigned(FDrawPanel) then
553 FDrawPanel.Visible := false;
554 end;
555
556 function TDBControlGrid.GetDataSource: TDataSource;
557 begin
558 Result:= FDataLink.DataSource;
559 end;
560
561 function TDBControlGrid.GetRecordCount: Integer;
562 begin
563 if assigned(FDataLink.DataSet) then
564 result := FDataLink.DataSet.RecordCount
565 else
566 result := 0;
567 end;
568
569 procedure TDBControlGrid.GetScrollbarParams(out aRange, aPage, aPos: Integer);
570 begin
571 if (FDatalink<>nil) and (FDataLink.DataSet <> nil) and FDatalink.Active then begin
572 if FDatalink.dataset.IsSequenced then begin
573 aRange := GetRecordCount + VisibleRowCount - 1;
574 aPage := VisibleRowCount;
575 if aPage<1 then aPage := 1;
576 if FDatalink.BOF then aPos := 0 else
577 if FDatalink.EOF then aPos := aRange
578 else
579 aPos := FDataLink.DataSet.RecNo - 1; // RecNo is 1 based
580 if aPos<0 then aPos:=0;
581 end else begin
582 aRange := 6;
583 aPage := 2;
584 if FDatalink.EOF then aPos := 4 else
585 if FDatalink.BOF then aPos := 0
586 else aPos := 2;
587 end;
588 end else begin
589 aRange := 0;
590 aPage := 0;
591 aPos := 0;
592 end;
593 end;
594
595 function TDBControlGrid.GridCanModify: boolean;
596 begin
597 result := not FDataLink.ReadOnly
598 and ValidDataSet and FDatalink.DataSet.CanModify;
599 end;
600
601 procedure TDBControlGrid.DoDrawRow(aRow: integer; aRect: TRect;
602 aState: TGridDrawState);
603 var CachedRow: TBitmap;
604 begin
605 CachedRow := FRowCache.GetRowImage(FSelectedRecNo,aRow-FDrawRow);
606 {if the row is in the cache then draw it - otherwise schedule a cache refresh cycle}
607 if CachedRow = nil then
608 begin
609 if not FCacheRefreshQueued then
610 begin
611 FCacheRefreshQueued := true;
612 Application.QueueAsyncCall(@DoMoveRecord,PtrInt(aRow));
613 end;
614 Canvas.FillRect(aRect);
615 end
616 else
617 Canvas.Draw(aRect.Left,aRect.Top,CachedRow)
618 end;
619
620 procedure TDBControlGrid.DoMoveRecord(Data: PtrInt);
621 var aRow: integer;
622 begin
623 if AppDestroying in Application.Flags then Exit;
624
625 FCacheRefreshQueued := false;
626 aRow := integer(Data);
627 FInCacheRefresh := true;
628 if assigned(FDataLink.DataSet) then
629 FDatalink.DataSet.MoveBy(aRow - FDrawRow);
630 end;
631
632 procedure TDBControlGrid.DoSetupDrawPanel(Data: PtrInt);
633 begin
634 if AppDestroying in Application.Flags then Exit;
635 SetupDrawPanel(FDrawRow);
636 end;
637
638 procedure TDBControlGrid.DoSendMouseClicks(Data: PtrInt);
639 var P: TPoint;
640 Control: TControl;
641 begin
642 if AppDestroying in Application.Flags then Exit;
643
644 if assigned(FDrawPanel) and (FLastMouse.X <> 0) then
645 begin
646 P := ClientToScreen(FLastMouse);
647 Control := FindControlAtPosition(P,false);
648 if (Control <> nil) and (Control is TWinControl) then
649 TWinControl(Control).SetFocus
650 else
651 Control := FDrawPanel;
652
653 P := Control.ScreenToClient(P);
654
655 LCLSendMouseDownMsg(Control,P.X,P.Y,FLastMouseButton,FLastMouseShiftState);
656 LCLSendMouseUpMsg(Control,P.X,P.Y, FLastMouseButton,FLastMouseShiftState);
657
658 end;
659 FLastMouse.X := 0;
660 end;
661
662 procedure TDBControlGrid.KeyDownHandler(Sender: TObject; var Key: Word;
663 Shift: TShiftState);
664 var Done: boolean;
665 AControl: TControl;
666 begin
667 if Visible and assigned(FDrawPanel) and FDrawPanel.Visible and FWeHaveFocus then
668 begin
669 AControl := ActiveControl;
670 if (AControl <> nil) and (AControl is TCustomComboBox)
671 and ((Key in [VK_UP,VK_DOWN]) or
672 (TCustomComboBox(AControl).DroppedDown and (Key = VK_RETURN)) or
673 ((TCustomComboBox(AControl).Text <> '') and (Key = VK_ESCAPE))) then
674 Exit; {ignore these keys if we are in a combobox}
675
676 if (AControl <> nil) and (AControl is TCustomMemo)
677 and (Key in [VK_RETURN,VK_UP,VK_DOWN]) then Exit; {Ignore Return in a CustomMemo}
678
679 if (AControl <> nil) and (AControl is TCustomGrid)
680 and (Key in [VK_RETURN,VK_UP,VK_DOWN,VK_TAB]) then Exit; {Ignore Return in a CustomMemo}
681
682 if (AControl <> nil) and ((AControl is TDateEdit) or (AControl is TCustomMaskedit))
683 and (Key in [VK_RETURN,VK_UP,VK_DOWN,
684 VK_ESCAPE,VK_LEFT,VK_RIGHT]) then Exit; {Ignore Return in a CustomMemo}
685 Done := false;
686 if assigned(FOnKeyDownHander) then
687 OnKeyDownHander(Sender,Key,Shift,Done);
688 if Done then Exit;
689
690 KeyDown(Key,Shift)
691 end;
692 end;
693
694 procedure TDBControlGrid.OnRecordChanged(Field: TField);
695 begin
696 UpdateActive
697 end;
698
699 procedure TDBControlGrid.OnCheckBrowseMode(aDataSet: TDataSet);
700 var RecNo: integer;
701 begin
702 if assigned(FDrawPanel) and (aDataSet.RecNo > 0)
703 and (FModified or (FRowCache.IsEmpty(aDataSet.RecNo))) then
704 begin
705 RecNo := aDataSet.RecNo;
706 Application.ProcessMessages;
707 if RecNo = aDataSet.RecNo then {Guard against sudden changes}
708 FRowCache.Add2Cache(RecNo,FDrawPanel);
709 end;
710 end;
711
712 procedure TDBControlGrid.OnDataSetChanged(aDataSet: TDataSet);
713 begin
714 if aDataSet.State = dsBrowse then
715 begin
716 if GetRecordCount = 0 then
717 begin
718 {Must be closed/reopened}
719 FRowCache.ClearCache;
720 FSelectedRow := 0;
721 end
722 else
723 if FLastRecordCount > GetRecordCount then
724 begin
725 {must be delete}
726 FRowCache.MarkAsDeleted(FSelectedRecNo);
727 Dec(FSelectedRow);
728 end;
729 LayoutChanged;
730 end;
731 FLastRecordCount := GetRecordCount;
732 if aDataSet.State = dsInsert then
733 begin
734 FRequiredRecNo := aDataSet.RecNo + 1;
735 Application.QueueAsyncCall(@DoSelectNext,0);
736 end;
737 UpdateActive
738 end;
739
740 procedure TDBControlGrid.OnDataSetOpen(aDataSet: TDataSet);
741 begin
742 LinkActive(true);
743 UpdateActive;
744 end;
745
746 procedure TDBControlGrid.OnDataSetClose(aDataSet: TDataSet);
747 begin
748 LinkActive(false);
749 end;
750
751 procedure TDBControlGrid.OnDrawPanelResize(Sender: TObject);
752 begin
753 FRowCache.Height := FDrawPanel.Height;
754 DefaultRowHeight := FDrawPanel.Height;
755 end;
756
757 procedure TDBControlGrid.OnEditingChanged(aDataSet: TDataSet);
758 begin
759 FModified := true;
760 end;
761
762 procedure TDBControlGrid.OnInvalidDataSet(aDataSet: TDataSet);
763 begin
764 LinkActive(False);
765 end;
766
767 procedure TDBControlGrid.OnInvalidDataSource(aDataSet: TDataset);
768 begin
769 LinkActive(False);
770 end;
771
772 procedure TDBControlGrid.OnLayoutChanged(aDataSet: TDataSet);
773 begin
774 LayoutChanged;
775 end;
776
777 procedure TDBControlGrid.OnNewDataSet(aDataSet: TDataset);
778 begin
779 LinkActive(True);
780 UpdateActive;
781 end;
782
783 procedure TDBControlGrid.OnDataSetScrolled(aDataSet: TDataSet; Distance: Integer);
784 begin
785 UpdateScrollBarRange;
786 if Distance <> 0 then
787 begin
788 FDrawRow := FixedRows + FDataLink.ActiveRecord;
789
790 if not FInCacheRefresh then
791 begin
792 Row := FDrawRow;
793 FSelectedRow := FDrawRow;
794 FSelectedRecNo := aDataSet.RecNo;
795 SetupDrawPanel(FDrawRow);
796 end
797 else
798 Application.QueueAsyncCall(@DoSetupDrawPanel,0);
799 end
800 else
801 UpdateActive;
802 end;
803
804 procedure TDBControlGrid.OnUpdateData(aDataSet: TDataSet);
805 begin
806 UpdateData;
807 end;
808
809 procedure TDBControlGrid.SetDataSource(AValue: TDataSource);
810 begin
811 if AValue = FDatalink.Datasource then Exit;
812 FDataLink.DataSource := AValue;
813 UpdateActive;
814 end;
815
816 procedure TDBControlGrid.SetDrawPanel(AValue: TWinControl);
817 var theForm: TWinControl;
818 begin
819 if FDrawPanel = AValue then Exit;
820 if FDrawPanel <> nil then
821 begin
822 RemoveFreeNotification(FDrawPanel);
823 FDrawPanel.RemoveAllHandlersOfObject(self);
824 theForm := Parent;
825 while not ((theForm is TCustomForm) or (theForm is TCustomFrame))
826 and (theForm.Parent <> nil) do
827 theForm := theForm.Parent;
828 FDrawPanel.Parent := theForm;
829 end;
830 FRowCache.ClearCache;
831 try
832 FDrawPanel := AValue;
833 if assigned(FDrawPanel) then
834 begin
835 FDrawPanel.Parent := self;
836 DefaultRowHeight := FDrawPanel.Height;
837 if csDesigning in ComponentState then
838 UpdateDrawPanelBounds(0)
839 else
840 FDrawPanel.Visible := false;
841 FRowCache.Height := FDrawPanel.Height;
842 FRowCache.Width := FDrawPanel.Width;
843 FDrawPanel.AddHandlerOnResize(@OnDrawPanelResize);
844 FreeNotification(FDrawPanel);
845 end;
846 except
847 FDrawPanel := nil;
848 raise;
849 end;
850 end;
851
852 procedure TDBControlGrid.SetOptions(AValue: TPanelGridOptions);
853 begin
854 if FOptions = AValue then Exit;
855 FOptions := AValue;
856 if dgpIndicator in FOptions then
857 begin
858 FixedCols := 1;
859 ColWidths[0] := 12
860 end
861 else
862 FixedCols := 0;
863 end;
864
865 procedure TDBControlGrid.SetupDrawPanel(aRow: integer);
866 begin
867 if FDrawPanel = nil then Exit;
868 if ValidDataSet and FRowCache.AlternateColor[FDataLink.DataSet.RecNo] then
869 FDrawPanel.Color := AlternateColor
870 else
871 FDrawPanel.Color := self.Color;
872 FDrawPanel.Visible := true;
873 UpdateDrawPanelBounds(aRow); {Position Draw Panel over expanded Row}
874 Invalidate;
875 end;
876
877 function TDBControlGrid.UpdateGridCounts: Integer;
878 var
879 RecCount: Integer;
880 FRCount, FCCount: Integer;
881 begin
882 BeginUpdate;
883 try
884 FRCount := 0;
885 if dgpIndicator in FOptions then
886 FCCount := 1
887 else
888 FCCount := 0;
889 if FDataLink.Active then begin
890 UpdateBufferCount;
891 RecCount := FDataLink.RecordCount;
892 if RecCount<1 then
893 RecCount := 1;
894 end else begin
895 RecCount := 0;
896 if FRCount=0 then
897 // need to be large enough to hold indicator
898 // if there is one, and if there are no titles
899 RecCount := FCCount;
900 end;
901
902 Inc(RecCount, FRCount);
903
904 RowCount := RecCount;
905 FixedRows := FRCount;
906 Result := RowCount ;
907 finally
908 EndUpdate;
909 end;
910 end;
911
912 procedure TDBControlGrid.UpdateBufferCount;
913 var
914 BCount: Integer;
915 begin
916 if FDataLink.Active then begin
917 BCount := GetBufferCount;
918 if BCount<1 then
919 BCount := 1;
920 FDataLink.BufferCount:= BCount;
921 end;
922 end;
923
924 procedure TDBControlGrid.UpdateDrawPanelBounds(aRow: integer);
925 var R: TRect;
926 begin
927 R := Rect(0,0,0,0);
928 if assigned(FDrawPanel) and
929 (aRow >= 0) and (aRow < RowCount) then
930 begin
931 // Upper and Lower bounds for this row
932 ColRowToOffSet(False, True, aRow, R.Top, R.Bottom);
933 //Bounds for visible Column
934 ColRowToOffSet(True,True,ColCount-1,R.Left,R.RIght);
935 FDrawPanel.BoundsRect := R;
936 end;
937 end;
938
939 procedure TDBControlGrid.UpdateScrollbarRange;
940 var
941 aRange, aPage, aPos: Integer;
942 ScrollInfo: TScrollInfo;
943 begin
944
945 if not HandleAllocated then exit;
946
947
948 GetScrollBarParams(aRange, aPage, aPos);
949
950 FillChar(ScrollInfo, SizeOf(ScrollInfo), 0);
951 ScrollInfo.cbSize := SizeOf(ScrollInfo);
952
953 {TODO: try to move this out}
954 {$ifdef WINDOWS}
955 ScrollInfo.fMask := SIF_ALL or SIF_DISABLENOSCROLL;
956 ScrollInfo.ntrackPos := 0;
957 {$else}
958 ScrollInfo.fMask := SIF_ALL or SIF_UPDATEPOLICY;
959 //ScrollInfo.ntrackPos := SB_POLICY_CONTINUOUS;
960 ScrollInfo.ntrackPos := SB_POLICY_DISCONTINUOUS;
961 {$endif}
962 ScrollInfo.nMin := 0;
963 ScrollInfo.nMax := aRange;
964 ScrollInfo.nPos := Min(aPos,aRange-aPage);
965 ScrollInfo.nPage := aPage;
966 // the redraw argument of SetScrollInfo means under gtk
967 // if the scrollbar is visible or not, in windows it
968 // seems to mean if the scrollbar is redrawn or not
969 // to reflect the scrollbar changes made
970 SetScrollInfo(Handle, SB_VERT, ScrollInfo,
971 (ScrollBars in [ssBoth, ssVertical]) or
972 ((Scrollbars in [ssAutoVertical, ssAutoBoth]) and (aRange>aPAge))
973 );
974 FOldPosition := aPos;
975 end;
976
977 procedure TDBControlGrid.WMVScroll(var Message: TLMVScroll);
978 var
979 IsSeq: boolean;
980 aPos, aRange, aPage: Integer;
981 DeltaRec: integer;
982
983 function MaxPos: Integer;
984 begin
985 if IsSeq then
986 result := GetRecordCount - 1
987 else
988 result := 4;
989 end;
990
991 procedure DsMoveBy(Delta: Integer);
992 begin
993 FDataLink.DataSet.MoveBy(Delta);
994 GetScrollbarParams(aRange, aPage, aPos);
995 end;
996
997 procedure DsGoto(BOF: boolean);
998 begin
999 if BOF then FDatalink.DataSet.First
1000 else FDataLink.DataSet.Last;
1001 GetScrollbarParams(aRange, aPage, aPos);
1002 end;
1003
1004 function DsPos: boolean;
1005 begin
1006 result := false;
1007 aPos := Message.Pos;
1008 if aPos=FOldPosition then begin
1009 result := true;
1010 exit;
1011 end;
1012 if aPos>=MaxPos then
1013 dsGoto(False)
1014 else if aPos<=0 then
1015 dsGoto(True)
1016 else if IsSeq then
1017 FDatalink.DataSet.RecNo := aPos + 1
1018 else begin
1019 DeltaRec := Message.Pos - FOldPosition;
1020 if DeltaRec=0 then begin
1021 result := true;
1022 exit
1023 end
1024 else if DeltaRec<-1 then
1025 DsMoveBy(-VisibleRowCount)
1026 else if DeltaRec>1 then
1027 DsMoveBy(VisibleRowCount)
1028 else
1029 DsMoveBy(DeltaRec);
1030 end;
1031 end;
1032
1033 begin
1034 if not FDatalink.Active or not assigned(FDataLink.DataSet) then exit;
1035
1036 IsSeq := FDatalink.DataSet.IsSequenced and not FDataLink.DataSet.Filtered;
1037 case Message.ScrollCode of
1038 SB_TOP:
1039 DsGoto(True);
1040 SB_BOTTOM:
1041 DsGoto(False);
1042 SB_PAGEUP:
1043 DsMoveBy(-VisibleRowCount);
1044 SB_LINEUP:
1045 DsMoveBy(-1);
1046 SB_LINEDOWN:
1047 DsMoveBy(1);
1048 SB_PAGEDOWN:
1049 DsMoveBy(VisibleRowCount);
1050 SB_THUMBPOSITION:
1051 if DsPos then
1052 exit;
1053 SB_THUMBTRACK:
1054 if not (FDatalink.DataSet.IsSequenced) or DsPos then
1055 begin
1056 exit;
1057 end;
1058 else begin
1059 Exit;
1060 end;
1061 end;
1062
1063 ScrollBarPosition(SB_VERT, aPos);
1064 FOldPosition:=aPos; end;
1065
1066 function TDBControlGrid.ISEOF: boolean;
1067 begin
1068 with FDatalink do
1069 result := ValidDataSet and DataSet.EOF;
1070 end;
1071
1072 function TDBControlGrid.ValidDataSet: boolean;
1073 begin
1074 result := FDatalink.Active And (FDatalink.DataSet<>nil)
1075 end;
1076
1077 function TDBControlGrid.InsertCancelable: boolean;
1078 begin
1079 Result := ValidDataSet;
1080 if Result then
1081 with FDatalink.DataSet do
1082 Result := (State=dsInsert) and not Modified ;
1083 end;
1084
1085 function TDBControlGrid.GetBufferCount: integer;
1086 begin
1087 Result := ClientHeight div DefaultRowHeight;
1088 end;
1089
1090 procedure TDBControlGrid.DoEnter;
1091 begin
1092 inherited DoEnter;
1093 FWeHaveFocus := true;
1094 end;
1095
1096 procedure TDBControlGrid.DoExit;
1097 begin
1098 FWeHaveFocus := false;
1099 if ValidDataSet and (dgpCancelOnExit in Options) and
1100 InsertCancelable then
1101 begin
1102 FDataLink.DataSet.Cancel;
1103 end;
1104 inherited DoExit;
1105 end;
1106
1107 procedure TDBControlGrid.DoGridResize;
1108 begin
1109 if Columns.Count = 0 then Exit;
1110
1111 if ColCount > 1 then
1112 Columns[0].Width := ClientWidth - ColWidths[0]
1113 else
1114 Columns[0].Width := ClientWidth;
1115
1116 FRowCache.Width := Columns[0].Width;
1117 UpdateDrawPanelBounds(Row);
1118 end;
1119
1120 procedure TDBControlGrid.DoOnResize;
1121 begin
1122 inherited DoOnResize;
1123 DoGridResize;
1124 end;
1125
1126 procedure TDBControlGrid.DoScrollDataSet(Data: PtrInt);
1127 begin
1128 if AppDestroying in Application.Flags then Exit;
1129 FDataLink.DataSet.MoveBy(integer(Data) - FDataLink.DataSet.RecNo);
1130 end;
1131
1132 procedure TDBControlGrid.DoSelectNext(Data: PtrInt);
1133 begin
1134 FDataLink.DataSet.MoveBy(1);
1135 end;
1136
1137 procedure TDBControlGrid.DrawAllRows;
1138 begin
1139 inherited DrawAllRows;
1140 if ValidDataSet and FDatalink.DataSet.Active then
1141 begin
1142 if FInCacheRefresh and not FCacheRefreshQueued then
1143 {We are at the end of a cache refresh cycle}
1144 begin
1145 if FRequiredRecNo > 0 then
1146 begin
1147 if FRequiredRecNo <> FDataLink.DataSet.RecNo then
1148 Application.QueueAsyncCall(@DoScrollDataSet,FRequiredRecNo);
1149 FRequiredRecNo := 0;
1150 end
1151 else
1152 if FDrawRow <> FSelectedRow then
1153 Application.QueueAsyncCall(@DoMoveRecord,FSelectedRow);
1154 end;
1155 FInCacheRefresh := false;
1156 end;
1157 end;
1158
1159 procedure TDBControlGrid.DrawRow(ARow: Integer);
1160 begin
1161 if (ARow>=FixedRows) and FDataLink.Active then
1162 FDrawingActiveRecord := (ARow = FDrawRow)
1163 else
1164 FDrawingActiveRecord := False;
1165 inherited DrawRow(ARow);
1166 end;
1167
1168 procedure TDBControlGrid.DrawCell(aCol, aRow: Integer; aRect: TRect;
1169 aState: TGridDrawState);
1170
1171 function GetDatasetState: TDataSetState;
1172 begin
1173 if ValidDataSet then
1174 result := FDataLink.DataSet.State
1175 else
1176 result := dsInactive;
1177 end;
1178
1179 var
1180 DataCol: Integer;
1181 begin
1182 PrepareCanvas(aCol, aRow, aState);
1183
1184 if aCol < FixedCols then
1185 DrawIndicator(Canvas,aRow, aRect,GetDataSetState,false)
1186 else
1187 if (FDrawPanel = nil) or not FDataLink.Active then
1188 DrawFillRect(Canvas,aRect)
1189 else
1190 if not FDrawingActiveRecord and FDataLink.Active then
1191 DoDrawRow(aRow,aRect,aState);
1192 {if we are drawing the active record then this is rendered by the Draw Panel
1193 i.e. a child control - so we need do nothing here}
1194
1195 DrawCellGrid(aCol, aRow, aRect, aState);
1196 end;
1197
1198 procedure TDBControlGrid.DrawIndicator(ACanvas: TCanvas; aRow: integer;
1199 R: TRect; Opt: TDataSetState; MultiSel: boolean);
1200 var
1201 dx,dy, x, y: Integer;
1202 procedure CenterY;
1203 begin
1204 y := R.Top + (R.Bottom-R.Top) div 2;
1205 end;
1206 procedure CenterX;
1207 begin
1208 X := R.Left + (R.Right-R.Left) div 2;
1209 end;
1210 procedure DrawEdit(clr: Tcolor);
1211 begin
1212 ACanvas.Pen.Color := clr;
1213 CenterY;
1214 CenterX;
1215 ACanvas.MoveTo(X-2, Y-Dy);
1216 ACanvas.LineTo(X+3, Y-Dy);
1217 ACanvas.MoveTo(X, Y-Dy);
1218 ACanvas.LineTo(X, Y+Dy);
1219 ACanvas.MoveTo(X-2, Y+Dy);
1220 ACanvas.LineTo(X+3, Y+Dy);
1221 end;
1222 procedure DrawBrowse;
1223 begin
1224 ACanvas.Brush.Color:=clBlack;
1225 ACanvas.Pen.Color:=clBlack;
1226 CenterY;
1227 x:= R.Left+3;
1228 if MultiSel then begin
1229 if BiDiMode = bdRightToLeft then begin
1230 ACanvas.Polyline([point(x+dx,y-dy), point(x,y),point(x+dx,y+dy), point(x+dx,y+dy-1)]);
1231 ACanvas.Polyline([point(x+dx,y-dy+1), point(x+1,y),point(x+dx,y+dy-1), point(x+dx,y+dy-2)]);
1232 CenterX;
1233 Dec(X,3);
1234 ACanvas.Ellipse(Rect(X+dx-2,Y-2,X+dx+2,Y+2));
1235 end else begin
1236 ACanvas.Polyline([point(x,y-dy), point(x+dx,y),point(x,y+dy), point(x,y+dy-1)]);
1237 ACanvas.Polyline([point(x,y-dy+1),point(x+dx-1,y),point(x, y+dy-1), point(x,y+dy-2)]);
1238 CenterX;
1239 Dec(X,3);
1240 ACanvas.Ellipse(Rect(X-2,Y-2,X+2,Y+2));
1241 end;
1242 end else begin
1243 if BiDiMode = bdRightToLeft then
1244 ACanvas.Polygon([point(x,y),point(x+dx,y-dy),point(x+dx, y+dy),point(x,y)])
1245 else
1246 ACanvas.Polygon([point(x,y-dy),point(x+dx,y),point(x, y+dy),point(x,y-dy)]);
1247 end;
1248 end;
1249
1250 begin
1251 ACanvas.Brush.Color := FixedColor;
1252 ACanvas.FillRect(R);
1253 if aRow <> Row then Exit;
1254
1255 dx := 6;
1256 dy := 6;
1257 case Opt of
1258 dsBrowse:
1259 DrawBrowse;
1260 dsEdit:
1261 if FDrawingActiveRecord then
1262 DrawEdit(clBlack)
1263 else
1264 DrawBrowse;
1265 dsInsert:
1266 if FDrawingActiveRecord then
1267 DrawEdit(clGreen)
1268 else
1269 DrawBrowse;
1270 else
1271 if MultiSel then begin
1272 ACanvas.Brush.Color:=clBlack;
1273 ACanvas.Pen.Color:=clBlack;
1274 CenterX;
1275 CenterY;
1276 ACanvas.Ellipse(Rect(X-3,Y-3,X+3,Y+3));
1277 end;
1278 end; end;
1279
1280 procedure TDBControlGrid.GridMouseWheel(shift: TShiftState; Delta: Integer);
1281 begin
1282 inherited GridMouseWheel(shift, Delta);
1283 self.SetFocus;
1284 if ValidDataSet then
1285 FDataLink.DataSet.MoveBy(Delta);
1286 end;
1287
1288 procedure TDBControlGrid.KeyDown(var Key: Word; Shift: TShiftState);
1289 type
1290 TOperation=(opMoveBy,opCancel,opAppend,opInsert,opDelete);
1291 var
1292 DeltaCol,DeltaRow: Integer;
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 Application.RemoveAsyncCalls(self);
1695 inherited Destroy;
1696 end;
1697
1698 function TDBControlGrid.MouseToRecordOffset(const x, y: Integer;
1699 out RecordOffset: Integer): TGridZone;
1700 var
1701 aCol,aRow: Integer;
1702 begin
1703 Result := MouseToGridZone(x, y);
1704
1705 RecordOffset := 0;
1706
1707 if (Result=gzInvalid) or (Result=gzFixedCells) then
1708 exit;
1709
1710 MouseToCell(x, y, aCol, aRow);
1711
1712 if (Result=gzFixedRows) or (Result=gzNormal) then
1713 RecordOffset := aRow - Row;
1714
1715 if (Result=gzFixedCols) or (Result=gzNormal) then begin
1716 aRow := ColumnIndexFromGridColumn(aCol);
1717 end;
1718 end;
1719
1720 function TDBControlGrid.ExecuteAction(AAction: TBasicAction): Boolean;
1721 begin
1722 Result := (DataLink <> nil)
1723 and DataLink.ExecuteAction(AAction);
1724 end;
1725
1726 function TDBControlGrid.UpdateAction(AAction: TBasicAction): Boolean;
1727 begin
1728 Result := (DataLink <> nil)
1729 and DataLink.UpdateAction(AAction);
1730 end;
1731
1732 end.
1733