home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 2000 March / pcp161b.iso / full / delphi / RUNIMAGE / DELPHI30 / SOURCE / VCL / DBCGRIDS.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-08-03  |  32.4 KB  |  1,165 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Visual Component Library                 }
  5. {                                                       }
  6. {       Copyright (c) 1996,97 Borland International     }
  7. {                                                       }
  8. {*******************************************************}
  9.  
  10. unit DBCGrids;
  11.  
  12. {$R-}
  13.  
  14. interface
  15.  
  16. uses SysUtils, Windows, Messages, Classes, Controls, Forms,
  17.   Graphics, Menus, DB;
  18.  
  19. type
  20.  
  21. { TDBCtrlGrid }
  22.  
  23.   TDBCtrlGrid = class;
  24.  
  25.   TDBCtrlGridLink = class(TDataLink)
  26.   private
  27.     FDBCtrlGrid: TDBCtrlGrid;
  28.   protected
  29.     procedure ActiveChanged; override;
  30.     procedure DataSetChanged; override;
  31.   public
  32.     constructor Create(DBCtrlGrid: TDBCtrlGrid);
  33.   end;
  34.  
  35.   TDBCtrlPanel = class(TWinControl)
  36.   private
  37.     FDBCtrlGrid: TDBCtrlGrid;
  38.     procedure CMControlListChange(var Message: TCMControlListChange); message CM_CONTROLLISTCHANGE;
  39.     procedure WMEraseBkgnd(var Message: TMessage); message WM_ERASEBKGND;
  40.     procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  41.     procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
  42.   protected
  43.     procedure CreateParams(var Params: TCreateParams); override;
  44.     procedure PaintWindow(DC: HDC); override;
  45.   public
  46.     constructor CreateLinked(DBCtrlGrid: TDBCtrlGrid);
  47.   end;
  48.  
  49.   TDBCtrlGridOrientation = (goVertical, goHorizontal);
  50.   TDBCtrlGridBorder = (gbNone, gbRaised);
  51.   TDBCtrlGridKey = (gkNull, gkEditMode, gkPriorTab, gkNextTab, gkLeft,
  52.     gkRight, gkUp, gkDown, gkScrollUp, gkScrollDown, gkPageUp, gkPageDown,
  53.     gkHome, gkEnd, gkInsert, gkAppend, gkDelete, gkCancel);
  54.  
  55.   TPaintPanelEvent = procedure(DBCtrlGrid: TDBCtrlGrid;
  56.     Index: Integer) of object;
  57.  
  58.   TDBCtrlGrid = class(TWinControl)
  59.   private
  60.     FDataLink: TDBCtrlGridLink;
  61.     FPanel: TDBCtrlPanel;
  62.     FCanvas: TCanvas;
  63.     FColCount: Integer;
  64.     FRowCount: Integer;
  65.     FPanelWidth: Integer;
  66.     FPanelHeight: Integer;
  67.     FPanelIndex: Integer;
  68.     FPanelCount: Integer;
  69.     FBitmapCount: Integer;
  70.     FPanelBitmap: HBitmap;
  71.     FSaveBitmap: HBitmap;
  72.     FPanelDC: HDC;
  73.     FOrientation: TDBCtrlGridOrientation;
  74.     FPanelBorder: TDBCtrlGridBorder;
  75.     FAllowInsert: Boolean;
  76.     FAllowDelete: Boolean;
  77.     FShowFocus: Boolean;
  78.     FFocused: Boolean;
  79.     FClicking: Boolean;
  80.     FSelColorChanged: Boolean;
  81.     FScrollBarKind: Integer;
  82.     FSelectedColor: TColor;
  83.     FOnPaintPanel: TPaintPanelEvent;
  84.     function AcquireFocus: Boolean;
  85.     procedure AdjustSize;
  86.     procedure CreatePanelBitmap;
  87.     procedure DataSetChanged(Reset: Boolean);
  88.     procedure DestroyPanelBitmap;
  89.     procedure DrawPanel(DC: HDC; Index: Integer);
  90.     procedure DrawPanelBackground(DC: HDC; const R: TRect; Erase, Selected: Boolean);
  91.     function FindNext(StartControl: TWinControl; GoForward: Boolean;
  92.      var WrapFlag: Integer): TWinControl;
  93.     function GetDataSource: TDataSource;
  94.     function GetEditMode: Boolean;
  95.     function GetPanelBounds(Index: Integer): TRect;
  96.     function PointInPanel(const P: TSmallPoint): Boolean;
  97.     procedure Reset;
  98.     procedure Scroll(Inc: Integer; ScrollLock: Boolean);
  99.     procedure ScrollMessage(var Message: TWMScroll);
  100.     procedure SelectNext(GoForward: Boolean);
  101.     procedure SetColCount(Value: Integer);
  102.     procedure SetDataSource(Value: TDataSource);
  103.     procedure SetEditMode(Value: Boolean);
  104.     procedure SetOrientation(Value: TDBCtrlGridOrientation);
  105.     procedure SetPanelBorder(Value: TDBCtrlGridBorder);
  106.     procedure SetPanelHeight(Value: Integer);
  107.     procedure SetPanelIndex(Value: Integer);
  108.     procedure SetPanelWidth(Value: Integer);
  109.     procedure SetRowCount(Value: Integer);
  110.     procedure SetSelectedColor(Value: TColor);
  111.     procedure UpdateDataLinks(Control: TControl; Inserting: Boolean);
  112.     procedure UpdateScrollBar;
  113.     procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
  114.     procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
  115.     procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
  116.     procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  117.     procedure WMEraseBkgnd(var Message: TMessage); message WM_ERASEBKGND;
  118.     procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  119.     procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
  120.     procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS;
  121.     procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  122.     procedure WMSize(var Message: TMessage); message WM_SIZE;
  123.     procedure CMChildKey(var Message: TCMChildKey); message CM_CHILDKEY;
  124.     procedure CMColorChanged(var Message: TMessage); message CM_COLORCHANGED;
  125.   protected
  126.     procedure CreateParams(var Params: TCreateParams); override;
  127.     procedure CreateWnd; override;
  128.     function GetChildParent: TComponent; override;
  129.     procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
  130.     procedure PaintPanel(Index: Integer); virtual;
  131.     procedure PaintWindow(DC: HDC); override;
  132.     procedure ReadState(Reader: TReader); override;
  133.     procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
  134.     property Panel: TDBCtrlPanel read FPanel;
  135.   public
  136.     constructor Create(AOwner: TComponent); override;
  137.     destructor Destroy; override;
  138.     procedure DoKey(Key: TDBCtrlGridKey);
  139.     procedure GetTabOrderList(List: TList); override;
  140.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  141.     property Canvas: TCanvas read FCanvas;
  142.     property EditMode: Boolean read GetEditMode write SetEditMode;
  143.     property PanelCount: Integer read FPanelCount;
  144.     property PanelIndex: Integer read FPanelIndex write SetPanelIndex;
  145.   published
  146.     property Align;
  147.     property AllowDelete: Boolean read FAllowDelete write FAllowDelete default True;
  148.     property AllowInsert: Boolean read FAllowInsert write FAllowInsert default True;
  149.     property ColCount: Integer read FColCount write SetColCount;
  150.     property Color;
  151.     property DataSource: TDataSource read GetDataSource write SetDataSource;
  152.     property DragCursor;
  153.     property DragMode;
  154.     property Enabled;
  155.     property Font;
  156.     property Orientation: TDBCtrlGridOrientation read FOrientation write SetOrientation default goVertical;
  157.     property PanelBorder: TDBCtrlGridBorder read FPanelBorder write SetPanelBorder default gbRaised;
  158.     property PanelHeight: Integer read FPanelHeight write SetPanelHeight;
  159.     property PanelWidth: Integer read FPanelWidth write SetPanelWidth;
  160.     property ParentColor;
  161.     property ParentFont;
  162.     property ParentShowHint;
  163.     property PopupMenu;
  164.     property TabOrder;
  165.     property TabStop default True;
  166.     property RowCount: Integer read FRowCount write SetRowCount;
  167.     property SelectedColor: TColor read FSelectedColor write SetSelectedColor
  168.       stored FSelColorChanged default clWindow;
  169.     property ShowFocus: Boolean read FShowFocus write FShowFocus default True;
  170.     property ShowHint;
  171.     property Visible;
  172.     property OnClick;
  173.     property OnDblClick;
  174.     property OnDragDrop;
  175.     property OnDragOver;
  176.     property OnEndDrag;
  177.     property OnEnter;
  178.     property OnExit;
  179.     property OnKeyDown;
  180.     property OnKeyPress;
  181.     property OnKeyUp;
  182.     property OnMouseDown;
  183.     property OnMouseMove;
  184.     property OnMouseUp;
  185.     property OnPaintPanel: TPaintPanelEvent read FOnPaintPanel write FOnPaintPanel;
  186.     property OnStartDrag;
  187.   end;
  188.  
  189. implementation
  190.  
  191. uses DBConsts;
  192.  
  193. { TDBCtrlGridLink }
  194.  
  195. constructor TDBCtrlGridLink.Create(DBCtrlGrid: TDBCtrlGrid);
  196. begin
  197.   inherited Create;
  198.   FDBCtrlGrid := DBCtrlGrid;
  199.   RPR;
  200. end;
  201.  
  202. procedure TDBCtrlGridLink.ActiveChanged;
  203. begin
  204.   FDBCtrlGrid.DataSetChanged(False);
  205. end;
  206.  
  207. procedure TDBCtrlGridLink.DataSetChanged;
  208. begin
  209.   FDBCtrlGrid.DataSetChanged(False);
  210. end;
  211.  
  212. { TDBCtrlPanel }
  213.  
  214. constructor TDBCtrlPanel.CreateLinked(DBCtrlGrid: TDBCtrlGrid);
  215. begin
  216.   inherited Create(DBCtrlGrid);
  217.   ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents,
  218.     csDoubleClicks, csOpaque, csReplicatable];
  219.   FDBCtrlGrid := DBCtrlGrid;
  220.   Parent := DBCtrlGrid;
  221. end;
  222.  
  223. procedure TDBCtrlPanel.CreateParams(var Params: TCreateParams);
  224. begin
  225.   inherited CreateParams(Params);
  226.   with Params.WindowClass do
  227.     style := style and not (CS_HREDRAW or CS_VREDRAW);
  228. end;
  229.  
  230. procedure TDBCtrlPanel.PaintWindow(DC: HDC);
  231. var
  232.   R: TRect;
  233.   Selected: Boolean;
  234. begin
  235.   with FDBCtrlGrid do
  236.   begin
  237.     if FDataLink.Active then
  238.     begin
  239.       Selected := (FDataLink.ActiveRecord = FPanelIndex);
  240.       DrawPanelBackground(DC, Self.ClientRect, True, Selected);
  241.       FCanvas.Handle := DC;
  242.       try
  243.         FCanvas.Font := Font;
  244.         FCanvas.Brush.Style := bsSolid;
  245.         FCanvas.Brush.Color := Color;
  246.         PaintPanel(FDataLink.ActiveRecord);
  247.         if FShowFocus and FFocused and Selected then
  248.         begin
  249.           R := Self.ClientRect;
  250.           if FPanelBorder = gbRaised then InflateRect(R, -2, -2);
  251.           FCanvas.Brush.Color := Color;
  252.           FCanvas.DrawFocusRect(R);
  253.         end;
  254.       finally
  255.         FCanvas.Handle := 0;
  256.       end;
  257.     end else
  258.       DrawPanelBackground(DC, Self.ClientRect, True, csDesigning in ComponentState);
  259.   end;
  260. end;
  261.  
  262. procedure TDBCtrlPanel.CMControlListChange(var Message: TCMControlListChange);
  263. begin
  264.   FDBCtrlGrid.UpdateDataLinks(Message.Control, Message.Inserting);
  265. end;
  266.  
  267. procedure TDBCtrlPanel.WMPaint(var Message: TWMPaint);
  268. var
  269.   DC: HDC;
  270.   PS: TPaintStruct;
  271. begin
  272.   if Message.DC = 0 then
  273.   begin
  274.     FDBCtrlGrid.CreatePanelBitmap;
  275.     try
  276.       Message.DC := FDBCtrlGrid.FPanelDC;
  277.       PaintHandler(Message);
  278.       Message.DC := 0;
  279.       DC := BeginPaint(Handle, PS);
  280.       BitBlt(DC, 0, 0, Width, Height, FDBCtrlGrid.FPanelDC, 0, 0, SRCCOPY);
  281.       EndPaint(Handle, PS);
  282.     finally
  283.       FDBCtrlGrid.DestroyPanelBitmap;
  284.     end;
  285.   end else
  286.     PaintHandler(Message);
  287. end;
  288.  
  289. procedure TDBCtrlPanel.WMNCHitTest(var Message: TWMNCHitTest);
  290. begin
  291.   if csDesigning in ComponentState then
  292.     Message.Result := HTCLIENT else
  293.     Message.Result := HTTRANSPARENT;
  294. end;
  295.  
  296. procedure TDBCtrlPanel.WMEraseBkgnd(var Message: TMessage);
  297. begin
  298.   Message.Result := 1;
  299. end;
  300.  
  301. { TDBCtrlGrid }
  302.  
  303. constructor TDBCtrlGrid.Create(AOwner: TComponent);
  304. begin
  305.   inherited Create(AOwner);
  306.   ControlStyle := [csOpaque, csDoubleClicks];
  307.   TabStop := True;
  308.   FDataLink := TDBCtrlGridLink.Create(Self);
  309.   FCanvas := TCanvas.Create;
  310.   FPanel := TDBCtrlPanel.CreateLinked(Self);
  311.   FColCount := 1;
  312.   FRowCount := 3;
  313.   FPanelWidth := 200;
  314.   FPanelHeight := 72;
  315.   FPanelBorder := gbRaised;
  316.   FAllowInsert := True;
  317.   FAllowDelete := True;
  318.   FShowFocus := True;
  319.   FSelectedColor := Color;
  320.   AdjustSize;
  321. end;
  322.  
  323. destructor TDBCtrlGrid.Destroy;
  324. begin
  325.   FCanvas.Free;
  326.   FDataLink.Free;
  327.   FDataLink := nil;
  328.   inherited Destroy;
  329. end;
  330.  
  331. function TDBCtrlGrid.AcquireFocus: Boolean;
  332. begin
  333.   Result := True;
  334.   if not (Focused or EditMode) then
  335.   begin
  336.     SetFocus;
  337.     Result := Focused;
  338.   end;
  339. end;
  340.  
  341. procedure TDBCtrlGrid.AdjustSize;
  342. var
  343.   W, H: Integer;
  344. begin
  345.   W := FPanelWidth * FColCount;
  346.   H := FPanelHeight * FRowCount;
  347.   if FOrientation = goVertical then
  348.     Inc(W, GetSystemMetrics(SM_CXVSCROLL)) else
  349.     Inc(H, GetSystemMetrics(SM_CYHSCROLL));
  350.   SetBounds(Left, Top, W, H);
  351.   Reset;
  352. end;
  353.  
  354. procedure TDBCtrlGrid.CreatePanelBitmap;
  355. var
  356.   DC: HDC;
  357. begin
  358.   if FBitmapCount = 0 then
  359.   begin
  360.     DC := GetDC(0);
  361.     FPanelBitmap := CreateCompatibleBitmap(DC, FPanel.Width, FPanel.Height);
  362.     ReleaseDC(0, DC);
  363.     FPanelDC := CreateCompatibleDC(0);
  364.     FSaveBitmap := SelectObject(FPanelDC, FPanelBitmap);
  365.   end;
  366.   Inc(FBitmapCount);
  367. end;
  368.  
  369. procedure TDBCtrlGrid.CreateParams(var Params: TCreateParams);
  370. begin
  371.   inherited CreateParams(Params);
  372.   with Params do
  373.   begin
  374.     Style := Style or WS_CLIPCHILDREN;
  375.     WindowClass.style := WindowClass.style and not (CS_HREDRAW or CS_VREDRAW);
  376.   end;
  377. end;
  378.  
  379. procedure TDBCtrlGrid.CreateWnd;
  380. begin
  381.   inherited CreateWnd;
  382.   if FOrientation = goVertical then
  383.     FScrollBarKind := SB_VERT else
  384.     FScrollBarKind := SB_HORZ;
  385.   if not FDataLink.Active then
  386.     SetScrollRange(Handle, FScrollBarKind, 0, 4, False);
  387.     UpdateScrollBar;
  388. end;
  389.  
  390. procedure TDBCtrlGrid.DataSetChanged(Reset: Boolean);
  391. var
  392.   NewPanelIndex, NewPanelCount: Integer;
  393.   FocusedControl: TWinControl;
  394.   R: TRect;
  395. begin
  396.   if csDesigning in ComponentState then
  397.   begin
  398.     NewPanelIndex := 0;
  399.     NewPanelCount := 1;
  400.   end else
  401.     if FDataLink.Active then
  402.     begin
  403.       NewPanelIndex := FDataLink.ActiveRecord;
  404.       NewPanelCount := FDataLink.RecordCount;
  405.       if NewPanelCount = 0 then NewPanelCount := 1;
  406.     end else
  407.     begin
  408.       NewPanelIndex := 0;
  409.       NewPanelCount := 0;
  410.     end;
  411.   FocusedControl := nil;
  412.   R := GetPanelBounds(NewPanelIndex);
  413.   if Reset or not HandleAllocated then FPanel.BoundsRect := R else
  414.   begin
  415.     FocusedControl := FindControl(GetFocus);
  416.     if (FocusedControl <> FPanel) and FPanel.ContainsControl(FocusedControl) then
  417.       FPanel.SetFocus else
  418.       FocusedControl := nil;
  419.     if NewPanelIndex <> FPanelIndex then
  420.     begin
  421.       SetWindowPos(FPanel.Handle, 0, R.Left, R.Top, R.Right - R.Left,
  422.         R.Bottom - R.Top, SWP_NOZORDER or SWP_NOREDRAW);
  423.       if NewPanelIndex >= FPanelCount then
  424.         { Force a full redraw of all children controls when inserting a
  425.           record and the panel is in a previously unused position }
  426.         RedrawWindow(FPanel.Handle, nil, 0, RDW_INVALIDATE or RDW_ALLCHILDREN);
  427.     end;
  428.   end;
  429.   FPanelIndex := NewPanelIndex;
  430.   FPanelCount := NewPanelCount;
  431.   FPanel.Visible := FPanelCount > 0;
  432.   FPanel.Invalidate;
  433.   if not Reset then
  434.   begin
  435.     Invalidate;
  436.     Update;
  437.   end;
  438.   UpdateScrollBar;
  439.   if (FocusedControl <> nil) and not FClicking and FocusedControl.CanFocus then
  440.     FocusedControl.SetFocus;
  441. end;
  442.  
  443. procedure TDBCtrlGrid.DestroyPanelBitmap;
  444. begin
  445.   Dec(FBitmapCount);
  446.   if FBitmapCount = 0 then
  447.   begin
  448.     SelectObject(FPanelDC, FSaveBitmap);
  449.     DeleteDC(FPanelDC);
  450.     DeleteObject(FPanelBitmap);
  451.   end;
  452. end;
  453.  
  454. procedure TDBCtrlGrid.DoKey(Key: TDBCtrlGridKey);
  455. var
  456.   HInc, VInc: Integer;
  457. begin
  458.   if FDataLink.Active then
  459.   begin
  460.     if FOrientation = goVertical then
  461.     begin
  462.       HInc := 1;
  463.       VInc := FColCount;
  464.     end else
  465.     begin
  466.       HInc := FRowCount;
  467.       VInc := 1;
  468.     end;
  469.     with FDataLink.DataSet do
  470.       case Key of
  471.         gkEditMode: EditMode := not EditMode;
  472.         gkPriorTab: SelectNext(False);
  473.         gkNextTab: SelectNext(True);
  474.         gkLeft: Scroll(-HInc, False);
  475.         gkRight: Scroll(HInc, False);
  476.         gkUp: Scroll(-VInc, False);
  477.         gkDown: Scroll(VInc, False);
  478.         gkScrollUp: Scroll(-VInc, True);
  479.         gkScrollDown: Scroll(VInc, True);
  480.         gkPageUp: Scroll(-FDataLink.BufferCount, True);
  481.         gkPageDown: Scroll(FDataLink.BufferCount, True);
  482.         gkHome: First;
  483.         gkEnd: Last;
  484.         gkInsert:
  485.           if FAllowInsert and CanModify then
  486.           begin
  487.             Insert;
  488.             EditMode := True;
  489.           end;
  490.         gkAppend:
  491.           if FAllowInsert and CanModify then
  492.           begin
  493.             Append;
  494.             EditMode := True;
  495.           end;
  496.         gkDelete:
  497.           if FAllowDelete and CanModify then
  498.           begin
  499.             Delete;
  500.             EditMode := False;
  501.           end;
  502.         gkCancel:
  503.           begin
  504.             Cancel;
  505.             EditMode := False;
  506.           end;
  507.       end;
  508.   end;
  509. end;
  510.  
  511. procedure TDBCtrlGrid.DrawPanel(DC: HDC; Index: Integer);
  512. var
  513.   SaveActive: Integer;
  514.   R: TRect;
  515. begin
  516.   R := GetPanelBounds(Index);
  517.   if Index < FPanelCount then
  518.   begin
  519.     SaveActive := FDataLink.ActiveRecord;
  520.     FDataLink.ActiveRecord := Index;
  521.     FPanel.PaintTo(FPanelDC, 0, 0);
  522.     FDataLink.ActiveRecord := SaveActive;
  523.   end else
  524.     DrawPanelBackground(FPanelDC, FPanel.ClientRect, True, False);
  525.   BitBlt(DC, R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top,
  526.     FPanelDC, 0, 0, SRCCOPY);
  527. end;
  528.  
  529. procedure TDBCtrlGrid.DrawPanelBackground(DC: HDC; const R: TRect;
  530.   Erase, Selected: Boolean);
  531. var
  532.   Brush: HBrush;
  533. begin
  534.   if Erase then
  535.   begin
  536.     if Selected then FPanel.Color := FSelectedColor
  537.     else FPanel.Color := Color;
  538.     Brush := CreateSolidBrush(ColorToRGB(FPanel.Color));
  539.     FillRect(DC, R, Brush);
  540.     DeleteObject(Brush);
  541.   end;
  542.   if FPanelBorder = gbRaised then
  543.     DrawEdge(DC, PRect(@R)^, BDR_RAISEDINNER, BF_RECT);
  544. end;
  545.  
  546. function TDBCtrlGrid.GetChildParent: TComponent;
  547. begin
  548.   Result := FPanel;
  549. end;
  550.  
  551. procedure TDBCtrlGrid.GetChildren(Proc: TGetChildProc; Root: TComponent);
  552. begin
  553.   FPanel.GetChildren(Proc, Root);
  554. end;
  555.  
  556. function TDBCtrlGrid.GetDataSource: TDataSource;
  557. begin
  558.   Result := FDataLink.DataSource;
  559. end;
  560.  
  561. function TDBCtrlGrid.GetEditMode: Boolean;
  562. begin
  563.   Result := not Focused and ContainsControl(FindControl(GetFocus));
  564. end;
  565.  
  566. function TDBCtrlGrid.GetPanelBounds(Index: Integer): TRect;
  567. var
  568.   Col, Row: Integer;
  569. begin
  570.   if FOrientation = goVertical then
  571.   begin
  572.     Col := Index mod FColCount;
  573.     Row := Index div FColCount;
  574.   end else
  575.   begin
  576.     Col := Index div FRowCount;
  577.     Row := Index mod FRowCount;
  578.   end;
  579.   Result.Left := FPanelWidth * Col;
  580.   Result.Top := FPanelHeight * Row;
  581.   Result.Right := Result.Left + FPanelWidth;
  582.   Result.Bottom := Result.Top + FPanelHeight;
  583. end;
  584.  
  585. procedure TDBCtrlGrid.GetTabOrderList(List: TList);
  586. begin
  587. end;
  588.  
  589. procedure TDBCtrlGrid.KeyDown(var Key: Word; Shift: TShiftState);
  590. var
  591.   GridKey: TDBCtrlGridKey;
  592. begin
  593.   inherited KeyDown(Key, Shift);
  594.   GridKey := gkNull;
  595.   case Key of
  596.     VK_LEFT: GridKey := gkLeft;
  597.     VK_RIGHT: GridKey := gkRight;
  598.     VK_UP: GridKey := gkUp;
  599.     VK_DOWN: GridKey := gkDown;
  600.     VK_PRIOR: GridKey := gkPageUp;
  601.     VK_NEXT: GridKey := gkPageDown;
  602.     VK_HOME: GridKey := gkHome;
  603.     VK_END: GridKey := gkEnd;
  604.     VK_RETURN, VK_F2: GridKey := gkEditMode;
  605.     VK_INSERT:
  606.       if GetKeyState(VK_CONTROL) >= 0 then
  607.         GridKey := gkInsert else
  608.         GridKey := gkAppend;
  609.     VK_DELETE: if GetKeyState(VK_CONTROL) < 0 then GridKey := gkDelete;
  610.     VK_ESCAPE: GridKey := gkCancel;
  611.   end;
  612.   DoKey(GridKey);
  613. end;
  614.  
  615. procedure TDBCtrlGrid.PaintWindow(DC: HDC);
  616. var
  617.   I: Integer;
  618.   Brush: HBrush;
  619. begin
  620.   if csDesigning in ComponentState then
  621.   begin
  622.     FPanel.Update;
  623.     Brush := CreateHatchBrush(HS_BDIAGONAL, ColorToRGB(clBtnShadow));
  624.     SetBkColor(DC, ColorToRGB(Color));
  625.     FillRect(DC, ClientRect, Brush);
  626.     DeleteObject(Brush);
  627.     for I := 1 to FColCount * FRowCount - 1 do
  628.       DrawPanelBackground(DC, GetPanelBounds(I), False, False);
  629.   end else
  630.   begin
  631.     CreatePanelBitmap;
  632.     try
  633.       for I := 0 to FColCount * FRowCount - 1 do
  634.         if (FPanelCount <> 0) and (I = FPanelIndex) then
  635.           FPanel.Update else
  636.           DrawPanel(DC, I);
  637.     finally
  638.       DestroyPanelBitmap;
  639.     end;
  640.   end;
  641.   { When width or height are not evenly divisible by panel size, fill the gaps }
  642.   if HandleAllocated then
  643.   begin
  644.     if (Height <> FPanel.Height * FRowCount) then
  645.     begin
  646.       Brush := CreateSolidBrush(ColorToRGB(Color));
  647.       FillRect(DC, Rect(0, FPanel.Height * FRowCount, Width, Height), Brush);
  648.       DeleteObject(Brush);
  649.     end;
  650.     if (Width <> FPanel.Width * FColCount) then
  651.     begin
  652.       Brush := CreateSolidBrush(ColorToRGB(Color));
  653.       FillRect(DC, Rect(FPanelWidth * FColCount, 0, Width, Height), Brush);
  654.       DeleteObject(Brush);
  655.     end;
  656.   end;
  657. end;
  658.  
  659. procedure TDBCtrlGrid.PaintPanel(Index: Integer);
  660. begin
  661.   if Assigned(FOnPaintPanel) then FOnPaintPanel(Self, Index);
  662. end;
  663.  
  664. function TDBCtrlGrid.PointInPanel(const P: TSmallPoint): Boolean;
  665. begin
  666.   Result := (FPanelCount > 0) and PtInRect(GetPanelBounds(FPanelIndex),
  667.     SmallPointToPoint(P));
  668. end;
  669.  
  670. procedure TDBCtrlGrid.ReadState(Reader: TReader);
  671. begin
  672.   inherited ReadState(Reader);
  673.   FPanel.FixupTabList;
  674. end;
  675.  
  676. procedure TDBCtrlGrid.Reset;
  677. begin
  678.   if csDesigning in ComponentState then
  679.     FDataLink.BufferCount := 1 else
  680.     FDataLink.BufferCount := FColCount * FRowCount;
  681.   DataSetChanged(True);
  682. end;
  683.  
  684. procedure TDBCtrlGrid.Scroll(Inc: Integer; ScrollLock: Boolean);
  685. var
  686.   NewIndex, ScrollInc, Adjust: Integer;
  687. begin
  688.   if FDataLink.Active and (Inc <> 0) then
  689.     with FDataLink.DataSet do
  690.       if State = dsInsert then
  691.       begin
  692.         UpdateRecord;
  693.         if Modified then Post else
  694.           if (Inc < 0) or not EOF then Cancel;
  695.       end else
  696.       begin
  697.         CheckBrowseMode;
  698.         DisableControls;
  699.         try
  700.           if ScrollLock then
  701.             if Inc > 0 then
  702.               MoveBy(Inc - MoveBy(Inc + FDataLink.BufferCount - FPanelIndex - 1))
  703.             else
  704.               MoveBy(Inc - MoveBy(Inc - FPanelIndex))
  705.           else
  706.           begin
  707.             NewIndex := FPanelIndex + Inc;
  708.             if (NewIndex >= 0) and (NewIndex < FDataLink.BufferCount) then
  709.               MoveBy(Inc)
  710.             else
  711.               if MoveBy(Inc) = Inc then
  712.               begin
  713.                 if FOrientation = goVertical then
  714.                   ScrollInc := FColCount else
  715.                   ScrollInc := FRowCount;
  716.                 if Inc > 0 then
  717.                   Adjust := ScrollInc - 1 - NewIndex mod ScrollInc
  718.                 else
  719.                   Adjust := 1 - ScrollInc - (NewIndex + 1) mod ScrollInc;
  720.                 MoveBy(-MoveBy(Adjust));
  721.               end;
  722.           end;
  723.           if (Inc = 1) and EOF and FAllowInsert and CanModify then Append;
  724.         finally
  725.           EnableControls;
  726.         end;
  727.       end;
  728. end;
  729.  
  730. procedure TDBCtrlGrid.ScrollMessage(var Message: TWMScroll);
  731. var
  732.   Key: TDBCtrlGridKey;
  733.   SI: TScrollInfo;
  734. begin
  735.   if AcquireFocus then
  736.   begin
  737.     Key := gkNull;
  738.     case Message.ScrollCode of
  739.       SB_LINEUP: Key := gkScrollUp;
  740.       SB_LINEDOWN: Key := gkScrollDown;
  741.       SB_PAGEUP: Key := gkPageUp;
  742.       SB_PAGEDOWN: Key := gkPageDown;
  743.       SB_TOP: Key := gkHome;
  744.       SB_BOTTOM: Key := gkEnd;
  745.       SB_THUMBPOSITION:
  746.         if FDataLink.Active and FDataLink.DataSet.IsSequenced then
  747.         begin
  748.           SI.cbSize := sizeof(SI);
  749.           SI.fMask := SIF_ALL;
  750.           GetScrollInfo(Self.Handle, FScrollBarKind, SI);
  751.           if SI.nTrackPos <= 1 then Key := gkHome
  752.           else if SI.nTrackPos >= FDataLink.DataSet.RecordCount then Key := gkEnd
  753.           else
  754.           begin
  755.             FDataLink.DataSet.RecNo := SI.nTrackPos;
  756.             Exit;
  757.           end;
  758.         end else
  759.         begin
  760.           case Message.Pos of
  761.             0: Key := gkHome;
  762.             1: Key := gkPageUp;
  763.             3: Key := gkPageDown;
  764.             4: Key := gkEnd;
  765.           end;
  766.         end;
  767.     end;
  768.     DoKey(Key);
  769.   end;
  770. end;
  771.  
  772. function TDBCtrlGrid.FindNext(StartControl: TWinControl; GoForward: Boolean;
  773.   var WrapFlag: Integer): TWinControl;
  774. var
  775.   I, StartIndex: Integer;
  776.   List: TList;
  777. begin
  778.   List := TList.Create;
  779.   try
  780.     StartIndex := 0;
  781.     I := 0;
  782.     Result := StartControl;
  783.     FPanel.GetTabOrderList(List);
  784.     if List.Count > 0 then
  785.     begin
  786.       StartIndex := List.IndexOf(StartControl);
  787.       if StartIndex = -1 then
  788.         if GoForward then
  789.           StartIndex := List.Count - 1 else
  790.           StartIndex := 0;
  791.       I := StartIndex;
  792.       repeat
  793.         if GoForward then
  794.         begin
  795.           Inc(I);
  796.           if I = List.Count then I := 0;
  797.         end else
  798.         begin
  799.           if I = 0 then I := List.Count;
  800.           Dec(I);
  801.         end;
  802.         Result := List[I];
  803.       until (Result.CanFocus and Result.TabStop) or (I = StartIndex);
  804.     end;
  805.     WrapFlag := 0;
  806.     if GoForward then
  807.     begin
  808.       if I <= StartIndex then WrapFlag := 1;
  809.     end else
  810.     begin
  811.       if I >= StartIndex then WrapFlag := -1;
  812.     end;
  813.   finally
  814.     List.Free;
  815.   end;
  816. end;
  817.  
  818. procedure TDBCtrlGrid.SelectNext(GoForward: Boolean);
  819. var
  820.   WrapFlag: Integer;
  821.   ParentForm: TCustomForm;
  822.   ActiveControl, Control: TWinControl;
  823. begin
  824.   ParentForm := GetParentForm(Self);
  825.   if ParentForm <> nil then
  826.   begin
  827.     ActiveControl := ParentForm.ActiveControl;
  828.     if ContainsControl(ActiveControl) then
  829.     begin
  830.       Control := FindNext(ActiveControl, GoForward, WrapFlag);
  831.       if not (FDataLink.DataSet.State in dsEditModes) then
  832.         FPanel.SetFocus;
  833.       try
  834.         if WrapFlag <> 0 then Scroll(WrapFlag, False);
  835.       except
  836.         ActiveControl.SetFocus;
  837.         raise;
  838.       end;
  839.       if not Control.CanFocus then
  840.         Control := FindNext(Control, GoForward, WrapFlag);
  841.       Control.SetFocus;
  842.     end;
  843.   end;
  844. end;
  845.  
  846. procedure TDBCtrlGrid.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
  847. var
  848.   ScrollWidth, ScrollHeight, NewPanelWidth, NewPanelHeight: Integer;
  849. begin
  850.   ScrollWidth := 0;
  851.   ScrollHeight := 0;
  852.   if FOrientation = goVertical then
  853.     ScrollWidth := GetSystemMetrics(SM_CXVSCROLL) else
  854.     ScrollHeight := GetSystemMetrics(SM_CYHSCROLL);
  855.   NewPanelWidth := (AWidth - ScrollWidth) div FColCount;
  856.   NewPanelHeight := (AHeight - ScrollHeight) div FRowCount;
  857.   if NewPanelWidth < 1 then NewPanelWidth := 1;
  858.   if NewPanelHeight < 1 then NewPanelHeight := 1;
  859.   if (FPanelWidth <> NewPanelWidth) or (FPanelHeight <> NewPanelHeight) then
  860.   begin
  861.     FPanelWidth := NewPanelWidth;
  862.     FPanelHeight := NewPanelHeight;
  863.     Reset;
  864.   end;
  865.   inherited SetBounds(ALeft, ATop, AWidth, AHeight);
  866. end;
  867.  
  868. procedure TDBCtrlGrid.SetColCount(Value: Integer);
  869. begin
  870.   if Value < 1 then Value := 1;
  871.   if Value > 100 then Value := 100;
  872.   if FColCount <> Value then
  873.   begin
  874.     FColCount := Value;
  875.     AdjustSize;
  876.   end;
  877. end;
  878.  
  879. procedure TDBCtrlGrid.SetDataSource(Value: TDataSource);
  880. begin
  881.   FDataLink.DataSource := Value;
  882.   UpdateDataLinks(FPanel, True);
  883. end;
  884.  
  885. procedure TDBCtrlGrid.SetEditMode(Value: Boolean);
  886. var
  887.   Control: TWinControl;
  888. begin
  889.   if GetEditMode <> Value then
  890.     if Value then
  891.     begin
  892.       Control := FPanel.FindNextControl(nil, True, True, False);
  893.       if Control <> nil then Control.SetFocus;
  894.     end else
  895.       SetFocus;
  896. end;
  897.  
  898. procedure TDBCtrlGrid.SetOrientation(Value: TDBCtrlGridOrientation);
  899. begin
  900.   if FOrientation <> Value then
  901.   begin
  902.     FOrientation := Value;
  903.     RecreateWnd;
  904.     AdjustSize;
  905.   end;
  906. end;
  907.  
  908. procedure TDBCtrlGrid.SetPanelBorder(Value: TDBCtrlGridBorder);
  909. begin
  910.   if FPanelBorder <> Value then
  911.   begin
  912.     FPanelBorder := Value;
  913.     Invalidate;
  914.     FPanel.Invalidate;
  915.   end;
  916. end;
  917.  
  918. procedure TDBCtrlGrid.SetPanelHeight(Value: Integer);
  919. begin
  920.   if Value < 1 then Value := 1;
  921.   if Value > 65535 then Value := 65535;
  922.   if FPanelHeight <> Value then
  923.   begin
  924.     FPanelHeight := Value;
  925.     AdjustSize;
  926.   end;
  927. end;
  928.  
  929. procedure TDBCtrlGrid.SetPanelIndex(Value: Integer);
  930. begin
  931.   if FDataLink.Active and (Value < PanelCount) then
  932.     FDataLink.DataSet.MoveBy(Value - FPanelIndex);
  933. end;
  934.  
  935. procedure TDBCtrlGrid.SetPanelWidth(Value: Integer);
  936. begin
  937.   if Value < 1 then Value := 1;
  938.   if Value > 65535 then Value := 65535;
  939.   if FPanelWidth <> Value then
  940.   begin
  941.     FPanelWidth := Value;
  942.     AdjustSize;
  943.   end;
  944. end;
  945.  
  946. procedure TDBCtrlGrid.SetRowCount(Value: Integer);
  947. begin
  948.   if Value < 1 then Value := 1;
  949.   if Value > 100 then Value := 100;
  950.   if FRowCount <> Value then
  951.   begin
  952.     FRowCount := Value;
  953.     AdjustSize;
  954.   end;
  955. end;
  956.  
  957. procedure TDBCtrlGrid.SetSelectedColor(Value: TColor);
  958. begin
  959.   if Value <> FSelectedColor then
  960.   begin
  961.     FSelectedColor := Value;
  962.     FSelColorChanged := Value <> Color;
  963.     Invalidate;
  964.     FPanel.Invalidate;
  965.   end;
  966. end;
  967.  
  968. procedure TDBCtrlGrid.UpdateDataLinks(Control: TControl; Inserting: Boolean);
  969. var
  970.   I: Integer;
  971.   DataLink: TDataLink;
  972. begin
  973.   if Inserting and not (csReplicatable in Control.ControlStyle) then
  974.     DatabaseError(SNotReplicatable);
  975.   DataLink := TDataLink(Control.Perform(CM_GETDATALINK, 0, 0));
  976.   if DataLink <> nil then
  977.   begin
  978.     DataLink.DataSourceFixed := False;
  979.     if Inserting then
  980.     begin
  981.       DataLink.DataSource := DataSource;
  982.       DataLink.DataSourceFixed := True;
  983.     end;
  984.   end;
  985.   if Control is TWinControl then
  986.     with TWinControl(Control) do
  987.       for I := 0 to ControlCount - 1 do
  988.         UpdateDataLinks(Controls[I], Inserting);
  989. end;
  990.  
  991. procedure TDBCtrlGrid.UpdateScrollBar;
  992. var
  993.   SIOld, SINew: TScrollInfo;
  994. begin
  995.   if FDatalink.Active and HandleAllocated then
  996.     with FDatalink.DataSet do
  997.     begin
  998.       SIOld.cbSize := sizeof(SIOld);
  999.       SIOld.fMask := SIF_ALL;
  1000.       GetScrollInfo(Self.Handle, FScrollBarKind, SIOld);
  1001.       SINew := SIOld;
  1002.       if IsSequenced then
  1003.       begin
  1004.         SINew.nMin := 1;
  1005.         SINew.nPage := Self.RowCount * Self.ColCount;
  1006.         SINew.nMax := RecordCount + SINew.nPage -1;
  1007.         if State in [dsInactive, dsBrowse, dsEdit] then
  1008.           SINew.nPos := RecNo;
  1009.       end
  1010.       else
  1011.       begin
  1012.         SINew.nMin := 0;
  1013.         SINew.nPage := 0;
  1014.         SINew.nMax := 4;
  1015.         if BOF then SINew.nPos := 0
  1016.         else if EOF then SINew.nPos := 4
  1017.         else SINew.nPos := 2;
  1018.       end;
  1019.       if (SINew.nMin <> SIOld.nMin) or (SINew.nMax <> SIOld.nMax) or
  1020.         (SINew.nPage <> SIOld.nPage) or (SINew.nPos <> SIOld.nPos) then
  1021.         SetScrollInfo(Self.Handle, FScrollBarKind, SINew, True);
  1022.     end;
  1023. end;
  1024.  
  1025. procedure TDBCtrlGrid.WMLButtonDown(var Message: TWMLButtonDown);
  1026. var
  1027.   I: Integer;
  1028.   P: TPoint;
  1029.   Window: HWnd;
  1030. begin
  1031.   if FDataLink.Active then
  1032.   begin
  1033.     P := SmallPointToPoint(Message.Pos);
  1034.     for I := 0 to FPanelCount - 1 do
  1035.       if (I <> FPanelIndex) and PtInRect(GetPanelBounds(I), P) then
  1036.       begin
  1037.         FClicking := True;
  1038.         try
  1039.           SetPanelIndex(I);
  1040.         finally
  1041.           FClicking := False;
  1042.         end;
  1043.         P := ClientToScreen(P);
  1044.         Window := WindowFromPoint(P);
  1045.         if IsChild(FPanel.Handle, Window) then
  1046.         begin
  1047.           Windows.ScreenToClient(Window, P);
  1048.           Message.Pos := PointToSmallPoint(P);
  1049.           with TMessage(Message) do SendMessage(Window, Msg, WParam, LParam);
  1050.           Exit;
  1051.         end;
  1052.         Break;
  1053.       end;
  1054.   end;
  1055.   if AcquireFocus then
  1056.   begin
  1057.     if PointInPanel(Message.Pos) then
  1058.     begin
  1059.       EditMode := False;
  1060.       Click;
  1061.     end;
  1062.     inherited;
  1063.   end;
  1064. end;
  1065.  
  1066. procedure TDBCtrlGrid.WMLButtonDblClk(var Message: TWMLButtonDblClk);
  1067. begin
  1068.   if PointInPanel(Message.Pos) then DblClick;
  1069.   inherited;
  1070. end;
  1071.  
  1072. procedure TDBCtrlGrid.WMHScroll(var Message: TWMHScroll);
  1073. begin
  1074.   ScrollMessage(Message);
  1075. end;
  1076.  
  1077. procedure TDBCtrlGrid.WMVScroll(var Message: TWMVScroll);
  1078. begin
  1079.   ScrollMessage(Message);
  1080. end;
  1081.  
  1082. procedure TDBCtrlGrid.WMEraseBkgnd(var Message: TMessage);
  1083. begin
  1084.   Message.Result := 1;
  1085. end;
  1086.  
  1087. procedure TDBCtrlGrid.WMPaint(var Message: TWMPaint);
  1088. begin
  1089.   PaintHandler(Message);
  1090. end;
  1091.  
  1092. procedure TDBCtrlGrid.WMSetFocus(var Message: TWMSetFocus);
  1093. begin
  1094.   FFocused := True;
  1095.   FPanel.Repaint;
  1096. end;
  1097.  
  1098. procedure TDBCtrlGrid.WMKillFocus(var Message: TWMKillFocus);
  1099. begin
  1100.   FFocused := False;
  1101.   FPanel.Repaint;
  1102. end;
  1103.  
  1104. procedure TDBCtrlGrid.WMGetDlgCode(var Message: TWMGetDlgCode);
  1105. begin
  1106.   Message.Result := DLGC_WANTARROWS or DLGC_WANTCHARS;
  1107. end;
  1108.  
  1109. procedure TDBCtrlGrid.WMSize(var Message: TMessage);
  1110. begin
  1111.   inherited;
  1112.   Invalidate;
  1113. end;
  1114.  
  1115. function GetShiftState: TShiftState;
  1116. begin
  1117.   Result := [];
  1118.   if GetKeyState(VK_SHIFT) < 0 then Include(Result, ssShift);
  1119.   if GetKeyState(VK_CONTROL) < 0 then Include(Result, ssCtrl);
  1120.   if GetKeyState(VK_MENU) < 0 then Include(Result, ssAlt);
  1121. end;
  1122.  
  1123. procedure TDBCtrlGrid.CMChildKey(var Message: TCMChildKey);
  1124. var
  1125.   ShiftState: TShiftState;
  1126.   GridKey: TDBCtrlGridKey;
  1127. begin
  1128.   with Message do
  1129.     if Sender <> Self then
  1130.     begin
  1131.       ShiftState := GetShiftState;
  1132.       if Assigned(OnKeyDown) then OnKeyDown(Sender, CharCode, ShiftState);
  1133.       GridKey := gkNull;
  1134.       case CharCode of
  1135.         VK_TAB:
  1136.           if not (ssCtrl in ShiftState) and
  1137.             (Sender.Perform(WM_GETDLGCODE, 0, 0) and DLGC_WANTTAB = 0) then
  1138.             if ssShift in ShiftState then
  1139.               GridKey := gkPriorTab else
  1140.               GridKey := gkNextTab;
  1141.         VK_RETURN:
  1142.           if (Sender.Perform(WM_GETDLGCODE, 0, 0) and DLGC_WANTALLKEYS = 0) then
  1143.             GridKey := gkEditMode;
  1144.         VK_F2: GridKey := gkEditMode;
  1145.         VK_ESCAPE: GridKey := gkCancel;
  1146.       end;
  1147.       if GridKey <> gkNull then
  1148.       begin
  1149.         DoKey(GridKey);
  1150.         Result := 1;
  1151.         Exit;
  1152.       end;
  1153.     end;
  1154.   inherited;
  1155. end;
  1156.  
  1157. procedure TDBCtrlGrid.CMColorChanged(var Message: TMessage);
  1158. begin
  1159.   inherited;
  1160.   if not FSelColorChanged then
  1161.     FSelectedColor := Color;
  1162. end;
  1163.  
  1164. end.
  1165.