ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/DBControlGrid.pas
Revision: 27
Committed: Tue Apr 14 13:10:23 2015 UTC (9 years ago) by tony
Content type: text/x-pascal
File size: 44179 byte(s)
Log Message:
Committing updates for Release R1-2-3

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