ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/DBControlGrid.pas
Revision: 35
Committed: Tue Jan 26 14:38:47 2016 UTC (8 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 46005 byte(s)
Log Message:
Committing updates for Release R1-3-2

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