ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/ibcontrols/DBControlGrid.pas
Revision: 31
Committed: Tue Jul 14 15:31:25 2015 UTC (9 years, 5 months ago) by tony
Content type: text/x-pascal
Original Path: ibx/trunk/ibcontrols/DBControlGrid.pas
File size: 45661 byte(s)
Log Message:
Committing updates for Release R1-3-0

File Contents

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