bsdbgrids.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:144k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. begin
  2.   with FGrid.Datalink.Datasource.Dataset do
  3.   begin
  4.     DisableControls;
  5.     try
  6.       for I := FList.Count-1 downto 0 do
  7.       begin
  8.         Bookmark := FList[I];
  9.         Delete;
  10.         FList.Delete(I);
  11.       end;
  12.     finally
  13.       EnableControls;
  14.     end;
  15.   end;
  16. end;
  17. function TbsBookmarkList.Refresh: Boolean;
  18. var
  19.   I: Integer;
  20. begin
  21.   Result := False;
  22.   with FGrid.DataLink.Datasource.Dataset do
  23.   try
  24.     CheckBrowseMode;
  25.     for I := FList.Count - 1 downto 0 do
  26.       if not BookmarkValid(TBookmark(FList[I])) then
  27.       begin
  28.         Result := True;
  29.         FList.Delete(I);
  30.       end;
  31.   finally
  32.     UpdateCursorPos;
  33.     if Result then FGrid.Invalidate;
  34.   end;
  35. end;
  36. procedure TbsBookmarkList.SetCurrentRowSelected(Value: Boolean);
  37. var
  38.   Index: Integer;
  39.   Current: TBookmarkStr;
  40. begin
  41.   Current := CurrentRow;
  42.   if (Length(Current) = 0) or (Find(Current, Index) = Value) then Exit;
  43.   if Value then
  44.     FList.Insert(Index, Current)
  45.   else
  46.     FList.Delete(Index);
  47.   FGrid.InvalidateRow(FGrid.Row);
  48. end;
  49. procedure TbsBookmarkList.StringsChanged(Sender: TObject);
  50. begin
  51.   FCache := '';
  52.   FCacheIndex := -1;
  53. end;
  54. { TbsSkinCustomDBGrid }
  55. var
  56.   DrawBitmap: TBitmap;
  57.   UserCount: Integer;
  58. procedure UsesBitmap;
  59. begin
  60.   if UserCount = 0 then
  61.     DrawBitmap := TBitmap.Create;
  62.   Inc(UserCount);
  63. end;
  64. procedure ReleaseBitmap;
  65. begin
  66.   Dec(UserCount);
  67.   if UserCount = 0 then DrawBitmap.Free;
  68. end;
  69. procedure WriteText(ACanvas: TCanvas; ARect: TRect; DX, DY: Integer;
  70.   const Text: string; Alignment: TAlignment; ARightToLeft: Boolean);
  71. const
  72.   AlignFlags : array [TAlignment] of Integer =
  73.     ( DT_LEFT or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX,
  74.       DT_RIGHT or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX,
  75.       DT_CENTER or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX );
  76.   RTL: array [Boolean] of Integer = (0, DT_RTLREADING);
  77. var
  78.   B, R: TRect;
  79.   Hold, Left: Integer;
  80.   I: TColorRef;
  81. begin
  82.   I := ColorToRGB(ACanvas.Brush.Color);
  83.   if GetNearestColor(ACanvas.Handle, I) = I then
  84.   begin                       { Use ExtTextOut for solid colors }
  85.     { In BiDi, because we changed the window origin, the text that does not
  86.       change alignment, actually gets its alignment changed. }
  87.     if (ACanvas.CanvasOrientation = coRightToLeft) and (not ARightToLeft) then
  88.       ChangeBiDiModeAlignment(Alignment);
  89.     case Alignment of
  90.       taLeftJustify:
  91.         Left := ARect.Left + DX;
  92.       taRightJustify:
  93.         Left := ARect.Right - ACanvas.TextWidth(Text) - 3;
  94.     else { taCenter }
  95.       Left := ARect.Left + (ARect.Right - ARect.Left) shr 1
  96.         - (ACanvas.TextWidth(Text) shr 1);
  97.     end;
  98.     ACanvas.TextRect(ARect, Left, ARect.Top + DY, Text);
  99.   end
  100.   else begin                  { Use FillRect and Drawtext for dithered colors }
  101.     DrawBitmap.Canvas.Lock;
  102.     try
  103.       with DrawBitmap, ARect do { Use offscreen bitmap to eliminate flicker and }
  104.       begin                     { brush origin tics in painting / scrolling.    }
  105.         Width := Max(Width, Right - Left);
  106.         Height := Max(Height, Bottom - Top);
  107.         R := Rect(DX, DY, Right - Left - 1, Bottom - Top - 1);
  108.         B := Rect(0, 0, Right - Left, Bottom - Top);
  109.       end;
  110.       with DrawBitmap.Canvas do
  111.       begin
  112.         Font := ACanvas.Font;
  113.         Font.Color := ACanvas.Font.Color;
  114.         Brush := ACanvas.Brush;
  115.         Brush.Style := bsSolid;
  116.         FillRect(B);
  117.         SetBkMode(Handle, TRANSPARENT);
  118.         if (ACanvas.CanvasOrientation = coRightToLeft) then
  119.           ChangeBiDiModeAlignment(Alignment);
  120.         DrawText(Handle, PChar(Text), Length(Text), R,
  121.           AlignFlags[Alignment] or RTL[ARightToLeft]);
  122.       end;
  123.       if (ACanvas.CanvasOrientation = coRightToLeft) then  
  124.       begin
  125.         Hold := ARect.Left;
  126.         ARect.Left := ARect.Right;
  127.         ARect.Right := Hold;
  128.       end;
  129.       ACanvas.CopyRect(ARect, DrawBitmap.Canvas, B);
  130.     finally
  131.       DrawBitmap.Canvas.Unlock;
  132.     end;
  133.   end;
  134. end;
  135. constructor TbsSkinCustomDBGrid.Create(AOwner: TComponent);
  136. var
  137.   Bmp: TBitmap;
  138. begin
  139.   inherited Create(AOwner);
  140.   inherited DefaultDrawing := False;
  141.   FMouseWheelSupport := False;
  142.   FSkinMessage := nil;
  143.   FAcquireFocus := True;
  144.   Bmp := TBitmap.Create;
  145.   try
  146.     Bmp.LoadFromResourceName(HInstance, bmArrow);
  147.     FIndicators := TImageList.CreateSize(Bmp.Width, Bmp.Height);
  148.     FIndicators.AddMasked(Bmp, clWhite);
  149.     Bmp.LoadFromResourceName(HInstance, bmEdit);
  150.     FIndicators.AddMasked(Bmp, clWhite);
  151.     Bmp.LoadFromResourceName(HInstance, bmInsert);
  152.     FIndicators.AddMasked(Bmp, clWhite);
  153.     Bmp.LoadFromResourceName(HInstance, bmMultiDot);
  154.     FIndicators.AddMasked(Bmp, clWhite);
  155.     Bmp.LoadFromResourceName(HInstance, bmMultiArrow);
  156.     FIndicators.AddMasked(Bmp, clWhite);
  157.   finally
  158.     Bmp.Free;
  159.   end;
  160.   FTitleOffset := 1;
  161.   FIndicatorOffset := 1;
  162.   FUpdateFields := True;
  163.   FOptions := [dgEditing, dgTitles, dgIndicator, dgColumnResize,
  164.     dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit];
  165.   if SysLocale.PriLangID = LANG_KOREAN then
  166.     Include(FOptions, dgAlwaysShowEditor);
  167.   DesignOptionsBoost := [goColSizing];
  168.   VirtualView := True;
  169.   UsesBitmap;
  170.   inherited Options := [goFixedHorzLine, goFixedVertLine, goHorzLine,
  171.     goVertLine, goColSizing, goColMoving, goTabs, goEditing];
  172.   FColumns := CreateColumns;
  173.   FVisibleColumns := TList.Create;
  174.   inherited RowCount := 2;
  175.   inherited ColCount := 2;
  176.   FDataLink := TbsGridDataLink.Create(Self);
  177.   Color := clWindow;
  178.   ParentColor := False;
  179.   FTitleFont := TFont.Create;
  180.   FTitleFont.OnChange := TitleFontChanged;
  181.   FSaveCellExtents := False;
  182.   FUserChange := True;
  183.   FDefaultDrawing := True;
  184.   FBookmarks := TbsBookmarkList.Create(Self);
  185.   HideEditor;
  186.   FPickListBoxSkinDataName := 'listbox';
  187. end;
  188. destructor TbsSkinCustomDBGrid.Destroy;
  189. begin
  190.   FColumns.Free;
  191.   FColumns := nil;
  192.   FVisibleColumns.Free;
  193.   FVisibleColumns := nil;
  194.   FDataLink.Free;
  195.   FDataLink := nil;
  196.   FIndicators.Free;
  197.   FTitleFont.Free;
  198.   FTitleFont := nil;
  199.   FBookmarks.Free;
  200.   FBookmarks := nil;
  201.   inherited Destroy;
  202.   ReleaseBitmap;
  203. end;
  204. procedure TbsSkinCustomDBGrid.ChangeSkinData;
  205. begin
  206.   inherited;
  207.   InternalLayout;
  208. end;
  209. procedure TbsSkinCustomDBGrid.WMMouseWheel;
  210. begin
  211.   if FMouseWheelSupport
  212.   then
  213.     begin
  214.       if DataSource.DataSet.Active
  215.       then
  216.         begin
  217.           if Message.WheelDelta > 0 then DataSource.DataSet.Prior
  218.           else
  219.           if Message.WheelDelta < -0 then DataSource.DataSet.Next;
  220.         end;
  221.     end
  222.   else
  223.     inherited;
  224. end;
  225. procedure TbsSkinCustomDBGrid.PickListBoxOnCheckButtonClick;
  226. begin
  227.   if InplaceEditor.Visible
  228.   then
  229.     begin
  230.       TDBGridInplaceEdit(InplaceEditor).CloseUp(True);
  231.     end;  
  232. end;
  233. procedure TbsSkinCustomDBGrid.SetHScrollBar;
  234. begin
  235.   inherited;
  236.   if HScrollBar <> nil then HScrollBar.PageSize := 0;
  237. end;
  238. procedure TbsSkinCustomDBGrid.UpdateScrollPos;
  239. begin
  240.   inherited UpdateScrollPos(False);
  241. end;
  242. procedure TbsSkinCustomDBGrid.UpdateScrollRange;
  243. begin
  244.   inherited UpdateScrollRange(False);
  245. end;
  246. function TbsSkinCustomDBGrid.AcquireFocus: Boolean;
  247. begin
  248.   Result := True;
  249.   if FAcquireFocus and CanFocus and not (csDesigning in ComponentState) then
  250.   begin
  251.     SetFocus;
  252.     Result := Focused or (InplaceEditor <> nil) and InplaceEditor.Focused;
  253.   end;
  254. end;
  255. function TbsSkinCustomDBGrid.RawToDataColumn(ACol: Integer): Integer;
  256. begin
  257.   Result := ACol - FIndicatorOffset;
  258. end;
  259. function TbsSkinCustomDBGrid.DataToRawColumn(ACol: Integer): Integer;
  260. begin
  261.   Result := ACol + FIndicatorOffset;
  262. end;
  263. function TbsSkinCustomDBGrid.AcquireLayoutLock: Boolean;
  264. begin
  265.   Result := (FUpdateLock = 0) and (FLayoutLock = 0);
  266.   if Result then BeginLayout;
  267. end;
  268. procedure TbsSkinCustomDBGrid.BeginLayout;
  269. begin
  270.   BeginUpdate;
  271.   if FLayoutLock = 0 then Columns.BeginUpdate;
  272.   Inc(FLayoutLock);
  273. end;
  274. procedure TbsSkinCustomDBGrid.BeginUpdate;
  275. begin
  276.   Inc(FUpdateLock);
  277. end;
  278. procedure TbsSkinCustomDBGrid.CancelLayout;
  279. begin
  280.   if FLayoutLock > 0 then
  281.   begin
  282.     if FLayoutLock = 1 then
  283.       Columns.EndUpdate;
  284.     Dec(FLayoutLock);
  285.     EndUpdate;
  286.   end;
  287. end;
  288. function TbsSkinCustomDBGrid.CanEditAcceptKey(Key: Char): Boolean;
  289. begin
  290.   with Columns[SelectedIndex] do
  291.     Result := FDatalink.Active and Assigned(Field) and Field.IsValidChar(Key);
  292. end;
  293. function TbsSkinCustomDBGrid.CanEditModify: Boolean;
  294. begin
  295.   Result := False;
  296.   if not ReadOnly and FDatalink.Active and not FDatalink.Readonly then
  297.   with Columns[SelectedIndex] do
  298.     if (not ReadOnly) and Assigned(Field) and Field.CanModify
  299.       and (not (Field.DataType in ftNonTextTypes) or Assigned(Field.OnSetText)) then
  300.     begin
  301.       FDatalink.Edit;
  302.       Result := FDatalink.Editing;
  303.       if Result then FDatalink.Modified;
  304.     end;
  305. end;
  306. function TbsSkinCustomDBGrid.CanEditShow: Boolean;
  307. begin
  308.   Result := (LayoutLock = 0) and inherited CanEditShow;
  309. end;
  310. procedure TbsSkinCustomDBGrid.CellClick(Column: TbsColumn);
  311. begin
  312.   if Assigned(FOnCellClick) then FOnCellClick(Column);
  313. end;
  314. procedure TbsSkinCustomDBGrid.ColEnter;
  315. begin
  316.   UpdateIme;
  317.   if Assigned(FOnColEnter) then FOnColEnter(Self);
  318. end;
  319. procedure TbsSkinCustomDBGrid.ColExit;
  320. begin
  321.   if Assigned(FOnColExit) then FOnColExit(Self);
  322. end;
  323. procedure TbsSkinCustomDBGrid.ColumnMoved(FromIndex, ToIndex: Longint);
  324. begin
  325.   FromIndex := RawToDataColumn(FromIndex);
  326.   ToIndex := RawToDataColumn(ToIndex);
  327.   Columns[FromIndex].Index := ToIndex;
  328.   if Assigned(FOnColumnMoved) then FOnColumnMoved(Self, FromIndex, ToIndex);
  329. end;
  330. procedure TbsSkinCustomDBGrid.ColWidthsChanged;
  331. var
  332.   I: Integer;
  333. begin
  334.   inherited ColWidthsChanged;
  335.   if (FDatalink.Active or (FColumns.State = csCustomized)) and
  336.     AcquireLayoutLock then
  337.   try
  338.     for I := FIndicatorOffset to ColCount - 1 do
  339.       FColumns[I - FIndicatorOffset].Width := ColWidths[I];
  340.   finally
  341.     EndLayout;
  342.   end;
  343. end;
  344. function TbsSkinCustomDBGrid.CreateColumns: TbsDBGridColumns;
  345. begin
  346.   Result := TbsDBGridColumns.Create(Self, TbsColumn);
  347. end;
  348. function TbsSkinCustomDBGrid.CreateEditor: TbsSkinInplaceEdit;
  349. begin
  350.   Result := TDBGridInplaceEdit.Create(Self);
  351. end;
  352. procedure TbsSkinCustomDBGrid.CreateWnd;
  353. begin
  354.   BeginUpdate;   { prevent updates in WMSize message that follows WMCreate }
  355.   try
  356.     inherited CreateWnd;
  357.   finally
  358.     EndUpdate;
  359.   end;
  360.   UpdateRowCount;
  361.   UpdateActive;
  362.   UpdateScrollBar;
  363.   FOriginalImeName := ImeName;
  364.   FOriginalImeMode := ImeMode;
  365. end;
  366. procedure TbsSkinCustomDBGrid.DataChanged;
  367. begin
  368.   if not HandleAllocated then Exit;
  369.   UpdateRowCount;
  370.   UpdateScrollBar;
  371.   UpdateActive;
  372.   InvalidateEditor;
  373.   ValidateRect(Handle, nil);
  374.   Invalidate;
  375. end;
  376. procedure TbsSkinCustomDBGrid.DefaultHandler(var Msg);
  377. var
  378.   P: TPopupMenu;
  379.   Cell: TGridCoord;
  380. begin
  381.   inherited DefaultHandler(Msg);
  382.   if TMessage(Msg).Msg = wm_RButtonUp then
  383.     with TWMRButtonUp(Msg) do
  384.     begin
  385.       Cell := MouseCoord(XPos, YPos);
  386.       if (Cell.X < FIndicatorOffset) or (Cell.Y < 0) then Exit;
  387.       P := Columns[RawToDataColumn(Cell.X)].PopupMenu;
  388.       if (P <> nil) and P.AutoPopup then
  389.       begin
  390.         SendCancelMode(nil);
  391.         P.PopupComponent := Self;
  392.         with ClientToScreen(SmallPointToPoint(Pos)) do
  393.           P.Popup(X, Y);
  394.         Result := 1;
  395.       end;
  396.     end;
  397. end;
  398. procedure TbsSkinCustomDBGrid.DeferLayout;
  399. var
  400.   M: TMsg;
  401. begin
  402.   if HandleAllocated and
  403.     not PeekMessage(M, Handle, cm_DeferLayout, cm_DeferLayout, pm_NoRemove) then
  404.     PostMessage(Handle, cm_DeferLayout, 0, 0);
  405.   CancelLayout;
  406. end;
  407. procedure TbsSkinCustomDBGrid.DefineFieldMap;
  408. var
  409.   I: Integer;
  410. begin
  411.   if FColumns.State = csCustomized then
  412.   begin   { Build the column/field map from the column attributes }
  413.     DataLink.SparseMap := True;
  414.     for I := 0 to FColumns.Count-1 do
  415.       FDataLink.AddMapping(FColumns[I].FieldName);
  416.   end
  417.   else   { Build the column/field map from the field list order }
  418.   begin
  419.     FDataLink.SparseMap := False;
  420.     with Datalink.Dataset do
  421.       for I := 0 to FieldList.Count - 1 do
  422.         with FieldList[I] do if Visible then Datalink.AddMapping(FullName);
  423.   end;
  424. end;
  425. function TbsSkinCustomDBGrid.UseRightToLeftAlignmentForField(const AField: TField;
  426.   Alignment: TAlignment): Boolean;
  427. begin
  428.   Result := False;
  429.   if IsRightToLeft then
  430.     Result := OkToChangeFieldAlignment(AField, Alignment);
  431. end;
  432. procedure TbsSkinCustomDBGrid.DefaultDrawDataCell(const Rect: TRect; Field: TField;
  433.   State: TGridDrawState);
  434. var
  435.   Alignment: TAlignment;
  436.   Value: string;
  437. begin
  438.   Alignment := taLeftJustify;
  439.   Value := '';
  440.   if Assigned(Field) then
  441.   begin
  442.     Alignment := Field.Alignment;
  443.     Value := Field.DisplayText;
  444.   end;
  445.   WriteText(Canvas, Rect, 2, 2, Value, Alignment,
  446.     UseRightToLeftAlignmentForField(Field, Alignment));
  447. end;
  448. procedure TbsSkinCustomDBGrid.DefaultDrawColumnCell(const Rect: TRect;
  449.   DataCol: Integer; Column: TbsColumn; State: TGridDrawState);
  450. var
  451.   Value: string;
  452. begin
  453.   Value := '';
  454.   if Assigned(Column.Field) then
  455.     Value := Column.Field.DisplayText;
  456.   WriteText(Canvas, Rect, 2, 2, Value, Column.Alignment,
  457.     UseRightToLeftAlignmentForField(Column.Field, Column.Alignment));
  458. end;
  459. procedure TbsSkinCustomDBGrid.ReadColumns(Reader: TReader);
  460. begin
  461.   Columns.Clear;
  462.   Reader.ReadValue;
  463.   Reader.ReadCollection(Columns);
  464. end;
  465. procedure TbsSkinCustomDBGrid.WriteColumns(Writer: TWriter);
  466. begin
  467.   if Columns.State = csCustomized then
  468.     Writer.WriteCollection(Columns)
  469.   else  // ancestor state is customized, ours is not
  470.     Writer.WriteCollection(nil);
  471. end;
  472. procedure TbsSkinCustomDBGrid.DefineProperties(Filer: TFiler);
  473. var
  474.   StoreIt: Boolean;
  475.   vState: TbsDBGridColumnsState;
  476. begin
  477.   vState := Columns.State;
  478.   if Filer.Ancestor = nil then
  479.     StoreIt := vState = csCustomized
  480.   else
  481.     if vState <> TbsSkinCustomDBGrid(Filer.Ancestor).Columns.State then
  482.       StoreIt := True
  483.     else
  484.       begin
  485.       {$IFDEF  VER140}
  486.         StoreIt := (vState = csCustomized) and
  487.          (not CollectionsEqual(Columns, TbsSkinCustomDBGrid(Filer.Ancestor).Columns, Self, TbsSkinCustomDBGrid(Filer.Ancestor)));
  488.       {$ELSE}
  489.         {$IFDEF  VER150}
  490.           StoreIt := (vState = csCustomized) and
  491.          (not CollectionsEqual(Columns, TbsSkinCustomDBGrid(Filer.Ancestor).Columns, Self, TbsSkinCustomDBGrid(Filer.Ancestor)));
  492.         {$ELSE}
  493.           StoreIt := (vState = csCustomized) and
  494.          (not CollectionsEqual(Columns, TbsSkinCustomDBGrid(Filer.Ancestor).Columns));
  495.         {$ENDIF}
  496.       {$ENDIF}
  497.       end;
  498.   Filer.DefineProperty('Columns', ReadColumns, WriteColumns, StoreIt);
  499. end;
  500. function TbsSkinCustomDBGrid.ColumnAtDepth(Col: TbsColumn; ADepth: Integer): TbsColumn;
  501. begin
  502.   Result := Col;
  503.   while (Result <> nil) and (Result.Depth > ADepth) do
  504.     Result := Result.ParentColumn;
  505. end;
  506. function TbsSkinCustomDBGrid.CalcTitleRect(Col: TbsColumn; ARow: Integer;
  507.   var MasterCol: TbsColumn): TRect;
  508. var
  509.   I,J: Integer;
  510.   InBiDiMode: Boolean;
  511.   DrawInfo: TbsGridDrawInfo;
  512. begin
  513.   MasterCol := ColumnAtDepth(Col, ARow);
  514.   if MasterCol = nil then Exit;
  515.   I := DataToRawColumn(MasterCol.Index);
  516.   if I >= LeftCol then
  517.     J := MasterCol.Depth
  518.   else
  519.   begin
  520.     I := LeftCol;
  521.     if Col.Depth > ARow then
  522.       J := ARow
  523.     else
  524.       J := Col.Depth;
  525.   end;
  526.   Result := CellRect(I, J);
  527.   InBiDiMode := UseRightToLeftAlignment and
  528.                 (Canvas.CanvasOrientation = coLeftToRight);
  529.   for I := Col.Index to Columns.Count-1 do
  530.   begin
  531.     if ColumnAtDepth(Columns[I], ARow) <> MasterCol then Break;
  532.     if not InBiDiMode then
  533.     begin
  534.       J := CellRect(DataToRawColumn(I), ARow).Right;
  535.       if J = 0 then Break;
  536.       Result.Right := Max(Result.Right, J);
  537.     end
  538.     else
  539.     begin
  540.       J := CellRect(DataToRawColumn(I), ARow).Left;
  541.       if J >= ClientWidth then Break;
  542.       Result.Left := J;
  543.     end;
  544.   end;
  545.   J := Col.Depth;
  546.   if (J <= ARow) and (J < FixedRows-1) then
  547.   begin
  548.     CalcFixedInfo(DrawInfo);
  549.     Result.Bottom := DrawInfo.Vert.FixedBoundary - DrawInfo.Vert.EffectiveLineWidth;
  550.   end;
  551. end;
  552. procedure TbsSkinCustomDBGrid.DrawCell;
  553. begin
  554.   if FIndex <> -1
  555.   then
  556.     DrawSkinCell(ACol, ARow, ARect, AState)
  557.   else
  558.     DrawDefaultCell(ACol, ARow, ARect, AState);
  559. end;
  560. procedure TbsSkinCustomDBGrid.DrawSkinCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
  561.   function RowIsMultiSelected: Boolean;
  562.   var
  563.     Index: Integer;
  564.   begin
  565.     Result := (dgMultiSelect in Options) and Datalink.Active and
  566.       FBookmarks.Find(Datalink.Datasource.Dataset.Bookmark, Index);
  567.   end;
  568.   procedure DrawTitleCell(ACol, ARow: Integer; Column: TbsColumn; var AState: TGridDrawState);
  569.   var
  570.     B: TBitMap;
  571.     TextRct, TitleRect: TRect;
  572.     MasterCol: TbsColumn;
  573.   begin
  574.     TitleRect := CalcTitleRect(Column, ARow, MasterCol);
  575.     B := TBitMap.Create;
  576.     CreateHSkinImage(FixedCellLeftOffset, FixedCellRightOffset,
  577.           B, Picture, FixedCellRect, RectWidth(TitleRect), RectHeight(TitleRect));
  578.     with B.Canvas do
  579.     begin
  580.       Font.Name := FixedFontName;
  581.       Font.Height := FixedFontHeight;
  582.       Font.Color := FixedFontColor;
  583.       Font.Style := FixedFontStyle;
  584.       Brush.Style := bsClear;
  585.     end;
  586.    TextRct := FixedCellTextRect;
  587.    Inc(TextRct.Right, B.Width - RectWidth(FixedCellRect));
  588.    if MasterCol <> nil
  589.    then
  590.     with MasterCol.Title do
  591.       if Alignment = taLeftJustify
  592.       then
  593.         SPDrawText2(B.Canvas, Caption, TextRct)
  594.       else
  595.         WriteText(B.Canvas, TextRct, 0, 0,
  596.           Caption, Alignment, IsRightToLeft);
  597.     Canvas.Draw(TitleRect.Left, TitleRect.Top, B);
  598.     B.Free;
  599.     AState := AState - [gdFixed];
  600.   end;
  601.   procedure DrawIndicatorCell(Indicator: Integer);
  602.   var
  603.     B: TBitMap;
  604.     IX, IY: Integer;
  605.     IRect: TRect;
  606.   begin
  607.     B := TBitMap.Create;
  608.     CreateHSkinImage(FixedCellLeftOffset, FixedCellRightOffset,
  609.           B, Picture, FixedCellRect, RectWidth(ARect), RectHeight(ARect));
  610.     IRect := FixedCellTextRect;
  611.     Inc(IRect.Right, B.Width - RectWidth(FixedCellRect));
  612.     IX := IRect.Left + RectWidth(IRect) div 2 - FIndicators.Width div 2;
  613.     IY := IRect.Top + RectHeight(IRect) div 2 - FIndicators.Height div 2;
  614.     FIndicators.Draw(B.Canvas, IX, IY, Indicator, True);
  615.     Canvas.Draw(ARect.Left, ARect.Top, B);
  616.     B.Free;
  617.   end;
  618.   procedure DrawFixedCell;
  619.   var
  620.     B: TBitMap;
  621.   begin
  622.     B := TBitMap.Create;
  623.     CreateHSkinImage(FixedCellLeftOffset, FixedCellRightOffset,
  624.           B, Picture, FixedCellRect, RectWidth(ARect), RectHeight(ARect));
  625.     Canvas.Draw(ARect.Left, ARect.Top, B);
  626.     B.Free;
  627.   end;
  628.   procedure DrawSelectedCell(AText: String; ADrawColumn: TbsColumn);
  629.   var
  630.     B: TBitMap;
  631.     TextRct: TRect;
  632.   begin
  633.     B := TBitMap.Create;
  634.     CreateHSkinImage(CellLeftOffset, CellRightOffset,
  635.           B, Picture, SelectCellRect, RectWidth(ARect), RectHeight(ARect));
  636.     with B.Canvas do
  637.     begin
  638.       Font.Name := FontName;
  639.       Font.Height := FontHeight;
  640.       Font.Color := SelectFontColor;
  641.       Font.Style := FontStyle;
  642.       Brush.Style := bsClear;
  643.     end;
  644.     TextRct := CellTextRect;
  645.     Inc(TextRct.Right, B.Width - RectWidth(SelectCellRect));
  646.     if ADrawColumn.Alignment = taLeftJustify
  647.     then
  648.       SPDrawText2(B.Canvas, AText, TextRct)
  649.     else
  650.       WriteText(B.Canvas, TextRct, 0, 0, AText, ADrawColumn.Alignment,
  651.                 UseRightToLeftAlignmentForField(ADrawColumn.Field, ADrawColumn.Alignment));
  652.     Canvas.Draw(ARect.Left, ARect.Top, B);
  653.     B.Free;
  654.   end;
  655.   procedure DrawFocusedCell(AText: String; ADrawColumn: TbsColumn);
  656.   var
  657.     B: TBitMap;
  658.     TextRct: TRect;
  659.   begin
  660.     B := TBitMap.Create;
  661.     CreateHSkinImage(CellLeftOffset, CellRightOffset,
  662.           B, Picture, FocusCellRect, RectWidth(ARect), RectHeight(ARect));
  663.     with B.Canvas do
  664.     begin
  665.       Font.Name := FontName;
  666.       Font.Height := FontHeight;
  667.       Font.Color := FocusFontColor;
  668.       Font.Style := FontStyle;
  669.       Brush.Style := bsClear;
  670.     end;
  671.     TextRct := CellTextRect;
  672.     Inc(TextRct.Right, B.Width - RectWidth(SelectCellRect));
  673.     if ADrawColumn.Alignment = taLeftJustify
  674.     then
  675.       SPDrawText2(B.Canvas, AText, TextRct)
  676.     else
  677.       WriteText(B.Canvas, TextRct, 0, 0, AText, ADrawColumn.Alignment,
  678.                 UseRightToLeftAlignmentForField(ADrawColumn.Field, ADrawColumn.Alignment));
  679.     Canvas.Draw(ARect.Left, ARect.Top, B);
  680.     B.Free;
  681.   end;
  682. var
  683.   OldActive: Integer;
  684.   Indicator: Integer;
  685.   Value: string;
  686.   DrawColumn: TbsColumn;
  687.   MultiSelected: Boolean;
  688. begin
  689.   Indicator := -1;
  690.   Dec(ARow, FTitleOffset);
  691.   Dec(ACol, FIndicatorOffset);
  692.   if (gdFixed in AState) and (ACol < 0) then
  693.   begin
  694.     if Assigned(DataLink) and DataLink.Active  then
  695.     begin
  696.       MultiSelected := False;
  697.       if ARow >= 0 then
  698.       begin
  699.         OldActive := FDataLink.ActiveRecord;
  700.         try
  701.           FDatalink.ActiveRecord := ARow;
  702.           MultiSelected := RowIsMultiselected;
  703.         finally
  704.           FDatalink.ActiveRecord := OldActive;
  705.         end;
  706.       end;
  707.       if (ARow = FDataLink.ActiveRecord) or MultiSelected then
  708.       begin
  709.         Indicator := 0;
  710.         if FDataLink.DataSet <> nil then
  711.           case FDataLink.DataSet.State of
  712.             dsEdit: Indicator := 1;
  713.             dsInsert: Indicator := 2;
  714.             dsBrowse:
  715.               if MultiSelected then
  716.                 if (ARow <> FDatalink.ActiveRecord) then
  717.                   Indicator := 3
  718.                 else
  719.                   Indicator := 4;  // multiselected and current row
  720.           end;
  721.         DrawIndicatorCell(Indicator);
  722.         if ARow = FDatalink.ActiveRecord then
  723.           FSelRow := ARow + FTitleOffset;
  724.       end;
  725.     end;
  726.   end
  727.   else with Canvas do
  728.   begin
  729.     DrawColumn := Columns[ACol];
  730.     if not DrawColumn.Showing then Exit;
  731.     if not (gdFixed in AState) then
  732.     begin
  733.       Font.Name := FontName;
  734.       Font.Height := FontHeight;
  735.       Font.Color := FontColor;
  736.       Font.Style := FontStyle;
  737.       Brush.Color := BGColor;
  738.     end;
  739.     if ARow < 0 then
  740.       DrawTitleCell(ACol, ARow + FTitleOffset, DrawColumn, AState)
  741.     else if (FDataLink = nil) or not FDataLink.Active then
  742.       FillRect(ARect)
  743.     else
  744.     begin
  745.       Value := '';
  746.       OldActive := FDataLink.ActiveRecord;
  747.       try
  748.         FDataLink.ActiveRecord := ARow;
  749.         if Assigned(DrawColumn.Field) then
  750.           Value := DrawColumn.Field.DisplayText;
  751.         if FDefaultDrawing then
  752.         if Focused and ((gdFocused in AState) or
  753.            ((gdSelected in AState) and (dgRowSelect in Options)))
  754.         then
  755.           DrawFocusedCell(Value, DrawColumn)
  756.         else
  757.         if gdSelected in AState
  758.         then
  759.           DrawSelectedCell(Value, DrawColumn)
  760.         else
  761.           WriteText(Canvas, ARect, 2, 2, Value, DrawColumn.Alignment,
  762.             UseRightToLeftAlignmentForField(DrawColumn.Field, DrawColumn.Alignment));
  763.         if Columns.State = csDefault then
  764.           DrawDataCell(ARect, DrawColumn.Field, AState);
  765.         DrawColumnCell(ARect, ACol, DrawColumn, AState);
  766.       finally
  767.         FDataLink.ActiveRecord := OldActive;
  768.       end;
  769.     end;
  770.   end;
  771.   if (gdFixed in AState) and ([dgRowLines, dgColLines] * Options =
  772.     [dgRowLines, dgColLines]) and (Indicator = -1)
  773.   then
  774.     DrawFixedCell;
  775. end;
  776. procedure TbsSkinCustomDBGrid.DrawDefaultCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
  777. var
  778.   FrameOffs: Byte;
  779.   function RowIsMultiSelected: Boolean;
  780.   var
  781.     Index: Integer;
  782.   begin
  783.     Result := (dgMultiSelect in Options) and Datalink.Active and
  784.       FBookmarks.Find(Datalink.Datasource.Dataset.Bookmark, Index);
  785.   end;
  786.   procedure DrawTitleCell(ACol, ARow: Integer; Column: TbsColumn; var AState: TGridDrawState);
  787.   const
  788.     ScrollArrows: array [Boolean, Boolean] of Integer =
  789.       ((DFCS_SCROLLRIGHT, DFCS_SCROLLLEFT), (DFCS_SCROLLLEFT, DFCS_SCROLLRIGHT));
  790.   var
  791.     MasterCol: TbsColumn;
  792.     TitleRect, TextRect, ButtonRect: TRect;
  793.     I: Integer;
  794.     InBiDiMode: Boolean;
  795.   begin
  796.     TitleRect := CalcTitleRect(Column, ARow, MasterCol);
  797.     if MasterCol = nil then
  798.     begin
  799.       Canvas.FillRect(ARect);
  800.       Exit;
  801.     end;
  802.     Canvas.Font := MasterCol.Title.Font;
  803.     Canvas.Brush.Color := MasterCol.Title.Color;
  804.     if [dgRowLines, dgColLines] * Options = [dgRowLines, dgColLines] then
  805.       InflateRect(TitleRect, -1, -1);
  806.     TextRect := TitleRect;
  807.     I := GetSystemMetrics(SM_CXHSCROLL);
  808.     if ((TextRect.Right - TextRect.Left) > I) and MasterCol.Expandable then
  809.     begin
  810.       Dec(TextRect.Right, I);
  811.       ButtonRect := TitleRect;
  812.       ButtonRect.Left := TextRect.Right;
  813.       I := SaveDC(Canvas.Handle);
  814.       try
  815.         Canvas.FillRect(ButtonRect);
  816.         InflateRect(ButtonRect, -1, -1);
  817.         IntersectClipRect(Canvas.Handle, ButtonRect.Left,
  818.           ButtonRect.Top, ButtonRect.Right, ButtonRect.Bottom);
  819.         InflateRect(ButtonRect, 1, 1);
  820.         { DrawFrameControl doesn't draw properly when orienatation has changed.
  821.           It draws as ExtTextOut does. }
  822.         InBiDiMode := Canvas.CanvasOrientation = coRightToLeft;
  823.         if InBiDiMode then { stretch the arrows box }
  824.           Inc(ButtonRect.Right, GetSystemMetrics(SM_CXHSCROLL) + 4);
  825.         DrawFrameControl(Canvas.Handle, ButtonRect, DFC_SCROLL,
  826.           ScrollArrows[InBiDiMode, MasterCol.Expanded] or DFCS_FLAT);
  827.       finally
  828.         RestoreDC(Canvas.Handle, I);
  829.       end;
  830.     end;
  831.     with MasterCol.Title do
  832.       WriteText(Canvas, TextRect, FrameOffs, FrameOffs, Caption, Alignment,
  833.         IsRightToLeft);
  834.     if [dgRowLines, dgColLines] * Options = [dgRowLines, dgColLines] then
  835.     begin
  836.       InflateRect(TitleRect, 1, 1);
  837.       DrawEdge(Canvas.Handle, TitleRect, BDR_RAISEDINNER, BF_BOTTOMRIGHT);
  838.       DrawEdge(Canvas.Handle, TitleRect, BDR_RAISEDINNER, BF_TOPLEFT);
  839.     end;
  840.     AState := AState - [gdFixed];  // prevent box drawing later
  841.   end;
  842. var
  843.   OldActive: Integer;
  844.   Indicator: Integer;
  845.   Highlight: Boolean;
  846.   Value: string;
  847.   DrawColumn: TbsColumn;
  848.   MultiSelected: Boolean;
  849.   ALeft: Integer;
  850. begin
  851.   if csLoading in ComponentState then
  852.   begin
  853.     Canvas.Brush.Color := Color;
  854.     Canvas.FillRect(ARect);
  855.     Exit;
  856.   end;
  857.   Dec(ARow, FTitleOffset);
  858.   Dec(ACol, FIndicatorOffset);
  859.   if (gdFixed in AState) and ([dgRowLines, dgColLines] * Options =
  860.     [dgRowLines, dgColLines]) then
  861.   begin
  862.     InflateRect(ARect, -1, -1);
  863.     FrameOffs := 1;
  864.   end
  865.   else
  866.     FrameOffs := 2;
  867.   if (gdFixed in AState) and (ACol < 0) then
  868.   begin
  869.     Canvas.Brush.Color := FixedColor;
  870.     Canvas.FillRect(ARect);
  871.     if Assigned(DataLink) and DataLink.Active  then
  872.     begin
  873.       MultiSelected := False;
  874.       if ARow >= 0 then
  875.       begin
  876.         OldActive := FDataLink.ActiveRecord;
  877.         try
  878.           FDatalink.ActiveRecord := ARow;
  879.           MultiSelected := RowIsMultiselected;
  880.         finally
  881.           FDatalink.ActiveRecord := OldActive;
  882.         end;
  883.       end;
  884.       if (ARow = FDataLink.ActiveRecord) or MultiSelected then
  885.       begin
  886.         Indicator := 0;
  887.         if FDataLink.DataSet <> nil then
  888.           case FDataLink.DataSet.State of
  889.             dsEdit: Indicator := 1;
  890.             dsInsert: Indicator := 2;
  891.             dsBrowse:
  892.               if MultiSelected then
  893.                 if (ARow <> FDatalink.ActiveRecord) then
  894.                   Indicator := 3
  895.                 else
  896.                   Indicator := 4;  // multiselected and current row
  897.           end;
  898.         ALeft := ARect.Right - FIndicators.Width - FrameOffs;
  899.         if Canvas.CanvasOrientation = coRightToLeft then Inc(ALeft);
  900.         FIndicators.Draw(Canvas, ALeft,
  901.           (ARect.Top + ARect.Bottom - FIndicators.Height) shr 1, Indicator, True);
  902.         if ARow = FDatalink.ActiveRecord then
  903.           FSelRow := ARow + FTitleOffset;
  904.       end;
  905.     end;
  906.   end
  907.   else with Canvas do
  908.   begin
  909.     DrawColumn := Columns[ACol];
  910.     if not DrawColumn.Showing then Exit;
  911.     if not (gdFixed in AState) then
  912.     begin
  913.       Font := DrawColumn.Font;
  914.       Brush.Color := DrawColumn.Color
  915.     end;
  916.     if ARow < 0 then
  917.       DrawTitleCell(ACol, ARow + FTitleOffset, DrawColumn, AState)
  918.     else if (FDataLink = nil) or not FDataLink.Active then
  919.       FillRect(ARect)
  920.     else
  921.     begin
  922.       Value := '';
  923.       OldActive := FDataLink.ActiveRecord;
  924.       try
  925.         FDataLink.ActiveRecord := ARow;
  926.         if Assigned(DrawColumn.Field) then
  927.           Value := DrawColumn.Field.DisplayText;
  928.         Highlight := HighlightCell(ACol, ARow, Value, AState);
  929.         if Highlight then
  930.         begin
  931.           Brush.Color := clHighlight;
  932.           Font.Color := clHighlightText;
  933.         end;
  934.         if not Enabled then
  935.           Font.Color := clGrayText;
  936.         // draw cell
  937.         if FDefaultDrawing then
  938.           WriteText(Canvas, ARect, 2, 2, Value, DrawColumn.Alignment,
  939.             UseRightToLeftAlignmentForField(DrawColumn.Field, DrawColumn.Alignment));
  940.         if Columns.State = csDefault then
  941.           DrawDataCell(ARect, DrawColumn.Field, AState);
  942.         DrawColumnCell(ARect, ACol, DrawColumn, AState);
  943.       finally
  944.         FDataLink.ActiveRecord := OldActive;
  945.       end;
  946.       if FDefaultDrawing and (gdSelected in AState)
  947.         and ((dgAlwaysShowSelection in Options) or Focused)
  948.         and not (csDesigning in ComponentState)
  949.         and not (dgRowSelect in Options)
  950.         and (UpdateLock = 0)
  951.         and (ValidParentForm(Self).ActiveControl = Self) then
  952.         Windows.DrawFocusRect(Handle, ARect);
  953.     end;
  954.   end;
  955.   if (gdFixed in AState) and ([dgRowLines, dgColLines] * Options =
  956.     [dgRowLines, dgColLines]) then
  957.   begin
  958.     InflateRect(ARect, 1, 1);
  959.     DrawEdge(Canvas.Handle, ARect, BDR_RAISEDINNER, BF_BOTTOMRIGHT);
  960.     DrawEdge(Canvas.Handle, ARect, BDR_RAISEDINNER, BF_TOPLEFT);
  961.   end;
  962. end;
  963. procedure TbsSkinCustomDBGrid.DrawDataCell(const Rect: TRect; Field: TField;
  964.   State: TGridDrawState);
  965. begin
  966.   if Assigned(FOnDrawDataCell) then FOnDrawDataCell(Self, Rect, Field, State);
  967. end;
  968. procedure TbsSkinCustomDBGrid.DrawColumnCell(const Rect: TRect; DataCol: Integer;
  969.   Column: TbsColumn; State: TGridDrawState);
  970. begin
  971.   if Assigned(OnDrawColumnCell) then
  972.     OnDrawColumnCell(Self, Rect, DataCol, Column, State);
  973. end;
  974. procedure TbsSkinCustomDBGrid.EditButtonClick;
  975. begin
  976.   if Assigned(FOnEditButtonClick) then
  977.     FOnEditButtonClick(Self)
  978.   else
  979.     ShowPopupEditor(Columns[SelectedIndex]);
  980. end;
  981. procedure TbsSkinCustomDBGrid.EditingChanged;
  982. begin
  983.   if dgIndicator in Options then InvalidateCell(0, FSelRow);
  984. end;
  985. procedure TbsSkinCustomDBGrid.EndLayout;
  986. begin
  987.   if FLayoutLock > 0 then
  988.   begin
  989.     try
  990.       try
  991.         if FLayoutLock = 1 then
  992.           InternalLayout;
  993.       finally
  994.         if FLayoutLock = 1 then
  995.           FColumns.EndUpdate;
  996.       end;
  997.     finally
  998.       Dec(FLayoutLock);
  999.       EndUpdate;
  1000.     end;
  1001.   end;
  1002. end;
  1003. procedure TbsSkinCustomDBGrid.EndUpdate;
  1004. begin
  1005.   if FUpdateLock > 0 then
  1006.     Dec(FUpdateLock);
  1007. end;
  1008. function TbsSkinCustomDBGrid.GetColField(DataCol: Integer): TField;
  1009. begin
  1010.   Result := nil;
  1011.   if (DataCol >= 0) and FDatalink.Active and (DataCol < Columns.Count) then
  1012.     Result := Columns[DataCol].Field;
  1013. end;
  1014. function TbsSkinCustomDBGrid.GetDataSource: TDataSource;
  1015. begin
  1016.   Result := FDataLink.DataSource;
  1017. end;
  1018. function TbsSkinCustomDBGrid.GetEditLimit: Integer;
  1019. begin
  1020.   Result := 0;
  1021.   if Assigned(SelectedField) and (SelectedField.DataType in [ftString, ftWideString]) then
  1022.     Result := SelectedField.Size;
  1023. end;
  1024. function TbsSkinCustomDBGrid.GetEditMask(ACol, ARow: Longint): string;
  1025. begin
  1026.   Result := '';
  1027.   if FDatalink.Active then
  1028.   with Columns[RawToDataColumn(ACol)] do
  1029.     if Assigned(Field) then
  1030.       Result := Field.EditMask;
  1031. end;
  1032. function TbsSkinCustomDBGrid.GetEditText(ACol, ARow: Longint): string;
  1033. begin
  1034.   Result := '';
  1035.   if FDatalink.Active then
  1036.   with Columns[RawToDataColumn(ACol)] do
  1037.     if Assigned(Field) then
  1038.       Result := Field.Text;
  1039.   FEditText := Result;
  1040. end;
  1041. function TbsSkinCustomDBGrid.GetFieldCount: Integer;
  1042. begin
  1043.   Result := FDatalink.FieldCount;
  1044. end;
  1045. function TbsSkinCustomDBGrid.GetFields(FieldIndex: Integer): TField;
  1046. begin
  1047.   Result := FDatalink.Fields[FieldIndex];
  1048. end;
  1049. function TbsSkinCustomDBGrid.GetFieldValue(ACol: Integer): string;
  1050. var
  1051.   Field: TField;
  1052. begin
  1053.   Result := '';
  1054.   Field := GetColField(ACol);
  1055.   if Field <> nil then Result := Field.DisplayText;
  1056. end;
  1057. function TbsSkinCustomDBGrid.GetSelectedField: TField;
  1058. var
  1059.   Index: Integer;
  1060. begin
  1061.   Index := SelectedIndex;
  1062.   if Index <> -1 then
  1063.     Result := Columns[Index].Field
  1064.   else
  1065.     Result := nil;
  1066. end;
  1067. function TbsSkinCustomDBGrid.GetSelectedIndex: Integer;
  1068. begin
  1069.   Result := RawToDataColumn(Col);
  1070. end;
  1071. function TbsSkinCustomDBGrid.HighlightCell(DataCol, DataRow: Integer;
  1072.   const Value: string; AState: TGridDrawState): Boolean;
  1073. var
  1074.   Index: Integer;
  1075. begin
  1076.   Result := False;
  1077.   if (dgMultiSelect in Options) and Datalink.Active then
  1078.     Result := FBookmarks.Find(Datalink.Datasource.Dataset.Bookmark, Index);
  1079.   if not Result then
  1080.     Result := (gdSelected in AState)
  1081.       and ((dgAlwaysShowSelection in Options) or Focused)
  1082.         { updatelock eliminates flicker when tabbing between rows }
  1083.       and ((UpdateLock = 0) or (dgRowSelect in Options));
  1084. end;
  1085. procedure TbsSkinCustomDBGrid.KeyDown(var Key: Word; Shift: TShiftState);
  1086. var
  1087.   KeyDownEvent: TKeyEvent;
  1088.   procedure ClearSelection;
  1089.   begin
  1090.     if (dgMultiSelect in Options) then
  1091.     begin
  1092.       FBookmarks.Clear;
  1093.       FSelecting := False;
  1094.     end;
  1095.   end;
  1096.   procedure DoSelection(Select: Boolean; Direction: Integer);
  1097.   var
  1098.     AddAfter: Boolean;
  1099.   begin
  1100.     AddAfter := False;
  1101.     BeginUpdate;
  1102.     try
  1103.       if (dgMultiSelect in Options) and FDatalink.Active then
  1104.         if Select and (ssShift in Shift) then
  1105.         begin
  1106.           if not FSelecting then
  1107.           begin
  1108.             FSelectionAnchor := FBookmarks.CurrentRow;
  1109.             FBookmarks.CurrentRowSelected := True;
  1110.             FSelecting := True;
  1111.             AddAfter := True;
  1112.           end
  1113.           else
  1114.           with FBookmarks do
  1115.           begin
  1116.             AddAfter := Compare(CurrentRow, FSelectionAnchor) <> -Direction;
  1117.             if not AddAfter then
  1118.               CurrentRowSelected := False;
  1119.           end
  1120.         end
  1121.         else
  1122.           ClearSelection;
  1123.       FDatalink.MoveBy(Direction);
  1124.       if AddAfter then FBookmarks.CurrentRowSelected := True;
  1125.     finally
  1126.       EndUpdate;
  1127.     end;
  1128.   end;
  1129.   procedure NextRow(Select: Boolean);
  1130.   begin
  1131.     with FDatalink.Dataset do
  1132.     begin
  1133.       if (State = dsInsert) and not Modified and not FDatalink.FModified then
  1134.         if FDataLink.EOF then Exit else Cancel
  1135.       else
  1136.         DoSelection(Select, 1);
  1137.       if FDataLink.EOF and CanModify and (not ReadOnly) and (dgEditing in Options) then
  1138.         Append;
  1139.     end;
  1140.   end;
  1141.   procedure PriorRow(Select: Boolean);
  1142.   begin
  1143.     with FDatalink.Dataset do
  1144.       if (State = dsInsert) and not Modified and FDataLink.EOF and
  1145.         not FDatalink.FModified then
  1146.         Cancel
  1147.       else
  1148.         DoSelection(Select, -1);
  1149.   end;
  1150.   procedure Tab(GoForward: Boolean);
  1151.   var
  1152.     ACol, Original: Integer;
  1153.   begin
  1154.     ACol := Col;
  1155.     Original := ACol;
  1156.     BeginUpdate;    { Prevent highlight flicker on tab to next/prior row }
  1157.     try
  1158.       while True do
  1159.       begin
  1160.         if GoForward then
  1161.           Inc(ACol) else
  1162.           Dec(ACol);
  1163.         if ACol >= ColCount then
  1164.         begin
  1165.           NextRow(False);
  1166.           ACol := FIndicatorOffset;
  1167.         end
  1168.         else if ACol < FIndicatorOffset then
  1169.         begin
  1170.           PriorRow(False);
  1171.           ACol := ColCount - FIndicatorOffset;
  1172.         end;
  1173.         if ACol = Original then Exit;
  1174.         if TabStops[ACol] then
  1175.         begin
  1176.           MoveCol(ACol, 0);
  1177.           Exit;
  1178.         end;
  1179.       end;
  1180.     finally
  1181.       EndUpdate;
  1182.     end;
  1183.   end;
  1184.   function DeletePrompt: Boolean;
  1185.   var
  1186.     Msg: string;
  1187.   begin
  1188.     if (FBookmarks.Count > 1) then
  1189.       Msg := SDeleteMultipleRecordsQuestion
  1190.     else
  1191.       Msg := SDeleteRecordQuestion;
  1192.     if FSkinMessage <> nil
  1193.     then
  1194.       Result := not (dgConfirmDelete in Options) or
  1195.       (FSkinMessage.MessageDlg(Msg, mtConfirmation, mbOKCancel, 0) <> idCancel)
  1196.     else
  1197.       Result := not (dgConfirmDelete in Options) or
  1198.       (MessageDlg(Msg, mtConfirmation, mbOKCancel, 0) <> idCancel);
  1199.   end;
  1200. const
  1201.   RowMovementKeys = [VK_UP, VK_PRIOR, VK_DOWN, VK_NEXT, VK_HOME, VK_END];
  1202. begin
  1203.   KeyDownEvent := OnKeyDown;
  1204.   if Assigned(KeyDownEvent) then KeyDownEvent(Self, Key, Shift);
  1205.   if not FDatalink.Active or not CanGridAcceptKey(Key, Shift) then Exit;
  1206.   if UseRightToLeftAlignment then
  1207.     if Key = VK_LEFT then
  1208.       Key := VK_RIGHT
  1209.     else if Key = VK_RIGHT then
  1210.       Key := VK_LEFT;
  1211.   with FDatalink.DataSet do
  1212.     if ssCtrl in Shift then
  1213.     begin
  1214.       if (Key in RowMovementKeys) then ClearSelection;
  1215.       case Key of
  1216.         VK_UP, VK_PRIOR: FDataLink.MoveBy(-FDatalink.ActiveRecord);
  1217.         VK_DOWN, VK_NEXT: FDataLink.MoveBy(FDatalink.BufferCount - FDatalink.ActiveRecord - 1);
  1218.         VK_LEFT: MoveCol(FIndicatorOffset, 1);
  1219.         VK_RIGHT: MoveCol(ColCount - 1, -1);
  1220.         VK_HOME: First;
  1221.         VK_END: Last;
  1222.         VK_DELETE:
  1223.           if (not ReadOnly) and not IsEmpty
  1224.             and CanModify and DeletePrompt then
  1225.           if FBookmarks.Count > 0 then
  1226.             FBookmarks.Delete
  1227.           else
  1228.             Delete;
  1229.       end
  1230.     end
  1231.     else
  1232.       case Key of
  1233.         VK_UP: PriorRow(True);
  1234.         VK_DOWN: NextRow(True);
  1235.         VK_LEFT:
  1236.           if dgRowSelect in Options then
  1237.             PriorRow(False) else
  1238.             MoveCol(Col - 1, -1);
  1239.         VK_RIGHT:
  1240.           if dgRowSelect in Options then
  1241.             NextRow(False) else
  1242.             MoveCol(Col + 1, 1);
  1243.         VK_HOME:
  1244.           if (ColCount = FIndicatorOffset+1)
  1245.             or (dgRowSelect in Options) then
  1246.           begin
  1247.             ClearSelection;
  1248.             First;
  1249.           end
  1250.           else
  1251.             MoveCol(FIndicatorOffset, 1);
  1252.         VK_END:
  1253.           if (ColCount = FIndicatorOffset+1)
  1254.             or (dgRowSelect in Options) then
  1255.           begin
  1256.             ClearSelection;
  1257.             Last;
  1258.           end
  1259.           else
  1260.             MoveCol(ColCount - 1, -1);
  1261.         VK_NEXT:
  1262.           begin
  1263.             ClearSelection;
  1264.             FDataLink.MoveBy(VisibleRowCount);
  1265.           end;
  1266.         VK_PRIOR:
  1267.           begin
  1268.             ClearSelection;
  1269.             FDataLink.MoveBy(-VisibleRowCount);
  1270.           end;
  1271.         VK_INSERT:
  1272.           if CanModify and (not ReadOnly) and (dgEditing in Options) then
  1273.           begin
  1274.             ClearSelection;
  1275.             Insert;
  1276.           end;
  1277.         VK_TAB: if not (ssAlt in Shift) then Tab(not (ssShift in Shift));
  1278.         VK_ESCAPE:
  1279.           begin
  1280.             if SysLocale.PriLangID = LANG_KOREAN then
  1281.               FIsESCKey := True;
  1282.             FDatalink.Reset;
  1283.             ClearSelection;
  1284.             if not (dgAlwaysShowEditor in Options) then HideEditor;
  1285.           end;
  1286.         VK_F2: EditorMode := True;
  1287.       end;
  1288. end;
  1289. procedure TbsSkinCustomDBGrid.KeyPress(var Key: Char);
  1290. begin
  1291.   FIsESCKey := False;
  1292.   if not (dgAlwaysShowEditor in Options) and (Key = #13) then
  1293.     FDatalink.UpdateData;
  1294.   inherited KeyPress(Key);
  1295. end;
  1296. procedure TbsSkinCustomDBGrid.InternalLayout;
  1297.   function FieldIsMapped(F: TField): Boolean;
  1298.   var
  1299.     X: Integer;
  1300.   begin
  1301.     Result := False;
  1302.     if F = nil then Exit;
  1303.     for X := 0 to FDatalink.FieldCount-1 do
  1304.       if FDatalink.Fields[X] = F then
  1305.       begin
  1306.         Result := True;
  1307.         Exit;
  1308.       end;
  1309.   end;
  1310.   procedure CheckForPassthroughs;  // check for Columns.State flip-flop
  1311.   var
  1312.     SeenPassthrough: Boolean;
  1313.     I, J: Integer;
  1314.     Column: TbsColumn;
  1315.   begin
  1316.     SeenPassthrough := False;
  1317.     for I := 0 to FColumns.Count-1 do
  1318.       if not FColumns[I].IsStored then
  1319.         SeenPassthrough := True
  1320.       else if SeenPassthrough then
  1321.       begin  // we have both persistent and non-persistent columns.  Kill the latter
  1322.         for J := FColumns.Count-1 downto 0 do
  1323.         begin
  1324.           Column := FColumns[J];
  1325.           if not Column.IsStored then
  1326.             Column.Free;
  1327.         end;
  1328.         Exit;
  1329.       end;
  1330.   end;
  1331.   procedure ReseTbsColumnFieldBindings;
  1332.   var
  1333.     I, J, K: Integer;
  1334.     Fld: TField;
  1335.     Column: TbsColumn;
  1336.   begin
  1337.     if FColumns.State = csDefault then
  1338.     begin
  1339.        { Destroy columns whose fields have been destroyed or are no longer
  1340.          in field map }
  1341.       if (not FDataLink.Active) and (FDatalink.DefaultFields) then
  1342.         FColumns.Clear
  1343.       else
  1344.         for J := FColumns.Count-1 downto 0 do
  1345.           with FColumns[J] do
  1346.           if not Assigned(Field)
  1347.             or not FieldIsMapped(Field) then Free;
  1348.       I := FDataLink.FieldCount;
  1349.       if (I = 0) and (FColumns.Count = 0) then Inc(I);
  1350.       for J := 0 to I-1 do
  1351.       begin
  1352.         Fld := FDatalink.Fields[J];
  1353.         if Assigned(Fld) then
  1354.         begin
  1355.           K := J;
  1356.           while (K < FColumns.Count) and (FColumns[K].Field <> Fld) do
  1357.             Inc(K);
  1358.           if K < FColumns.Count then
  1359.             Column := FColumns[K]
  1360.           else
  1361.           begin
  1362.             Column := FColumns.InternalAdd;
  1363.             Column.Field := Fld;
  1364.           end;
  1365.         end
  1366.         else
  1367.           Column := FColumns.InternalAdd;
  1368.         Column.Index := J;
  1369.       end;
  1370.     end
  1371.     else
  1372.     begin
  1373.       { Force columns to reaquire fields (in case dataset has changed) }
  1374.       for I := 0 to FColumns.Count-1 do
  1375.         FColumns[I].Field := nil;
  1376.     end;
  1377.   end;
  1378.   procedure MeasureTitleHeights;
  1379.   var
  1380.     I, J, K, D, B: Integer;
  1381.     RestoreCanvas: Boolean;
  1382.     Heights: array of Integer;
  1383.   begin
  1384.     RestoreCanvas := not HandleAllocated;
  1385.     if RestoreCanvas then
  1386.       Canvas.Handle := GetDC(0);
  1387.     try
  1388.       Canvas.Font := Font;
  1389.       // row heights
  1390.       if FIndex = -1
  1391.       then
  1392.         begin
  1393.           K := Canvas.TextHeight('Wg') + 3;
  1394.           if dgRowLines in Options then
  1395.           Inc(K, GridLineWidth);
  1396.           DefaultRowHeight := K
  1397.         end
  1398.       else
  1399.         DefaultRowHeight := SelectCellRect.Bottom - SelectCellRect.Top;
  1400.       B := GetSystemMetrics(SM_CYHSCROLL);
  1401.       if dgTitles in Options then
  1402.       begin
  1403.         SetLength(Heights, FTitleOffset+1);
  1404.         for I := 0 to FColumns.Count-1 do
  1405.         begin
  1406.           Canvas.Font := FColumns[I].Title.Font;
  1407.           D := FColumns[I].Depth;
  1408.           if D <= High(Heights) then
  1409.           begin
  1410.             // title height
  1411.             if FIndex = -1
  1412.             then
  1413.               begin
  1414.                 J := Canvas.TextHeight('Wg') + 4;
  1415.                 if FColumns[I].Expandable and (B > J) then
  1416.                 J := B;
  1417.               end
  1418.             else
  1419.               J := FixedCellRect.Bottom - FixedCellRect.Top;
  1420.             Heights[D] := Max(J, Heights[D]);
  1421.           end;
  1422.         end;
  1423.         if Heights[0] = 0 then
  1424.         begin
  1425.           Canvas.Font := FTitleFont;
  1426.           if FIndex = -1
  1427.           then
  1428.             Heights[0] := Canvas.TextHeight('Wg') + 4
  1429.           else
  1430.             Heights[0] := FixedCellRect.Bottom - FixedCellRect.Top;
  1431.         end;
  1432.         for I := 0 to High(Heights)-1 do
  1433.           RowHeights[I] := Heights[I];
  1434.       end;
  1435.     finally
  1436.       if RestoreCanvas then
  1437.       begin
  1438.         ReleaseDC(0,Canvas.Handle);
  1439.         Canvas.Handle := 0;
  1440.       end;
  1441.     end;
  1442.   end;
  1443. var
  1444.   I, J: Integer;
  1445. begin
  1446.   if (csLoading in ComponentState) then Exit;
  1447.   if HandleAllocated then KillMessage(Handle, cm_DeferLayout);
  1448.   CheckForPassthroughs;
  1449.   FIndicatorOffset := 0;
  1450.   if dgIndicator in Options then
  1451.     Inc(FIndicatorOffset);
  1452.   FDatalink.ClearMapping;
  1453.   if FDatalink.Active then DefineFieldMap;
  1454.   DoubleBuffered := (FDatalink.Dataset <> nil) and FDatalink.Dataset.ObjectView;
  1455.   ReseTbsColumnFieldBindings;
  1456.   FVisibleColumns.Clear;
  1457.   for I := 0 to FColumns.Count-1 do
  1458.     if FColumns[I].Showing then FVisibleColumns.Add(FColumns[I]);
  1459.   ColCount := FColumns.Count + FIndicatorOffset;
  1460.   inherited FixedCols := FIndicatorOffset;
  1461.   FTitleOffset := 0;
  1462.   if dgTitles in Options then
  1463.   begin
  1464.     FTitleOffset := 1;
  1465.     if (FDatalink <> nil) and (FDatalink.Dataset <> nil)
  1466.       and FDatalink.Dataset.ObjectView then
  1467.     begin
  1468.       for I := 0 to FColumns.Count-1 do
  1469.       begin
  1470.         if FColumns[I].Showing then
  1471.         begin
  1472.           J := FColumns[I].Depth;
  1473.           if J >= FTitleOffset then FTitleOffset := J+1;
  1474.         end;
  1475.       end;
  1476.     end;
  1477.   end;
  1478.   UpdateRowCount;
  1479.   MeasureTitleHeights;
  1480.   SetColumnAttributes;
  1481.   UpdateActive;
  1482.   Invalidate;
  1483. end;
  1484. procedure TbsSkinCustomDBGrid.LayoutChanged;
  1485. begin
  1486.   if AcquireLayoutLock then
  1487.     EndLayout;
  1488. end;
  1489. procedure TbsSkinCustomDBGrid.LinkActive(Value: Boolean);
  1490. var
  1491.   Comp: TComponent;
  1492.   I: Integer;
  1493. begin
  1494.   if not Value then HideEditor;
  1495.   FBookmarks.LinkActive(Value);
  1496.   try
  1497.     LayoutChanged;
  1498.   finally
  1499.     for I := ComponentCount-1 downto 0 do
  1500.     begin
  1501.       Comp := Components[I];   // Free all the popped-up subgrids
  1502.       if (Comp is TbsSkinCustomDBGrid)
  1503.         and (TbsSkinCustomDBGrid(Comp).DragKind = dkDock) then
  1504.         Comp.Free;
  1505.     end;
  1506.     UpdateScrollBar;
  1507.     if Value and (dgAlwaysShowEditor in Options) then ShowEditor;
  1508.   end;
  1509. end;
  1510. procedure TbsSkinCustomDBGrid.Loaded;
  1511. begin
  1512.   inherited Loaded;
  1513.   if FColumns.Count > 0 then
  1514.     ColCount := FColumns.Count;
  1515.   LayoutChanged;
  1516. end;
  1517. function TbsSkinCustomDBGrid.PtInExpandButton(X,Y: Integer; var MasterCol: TbsColumn): Boolean;
  1518. var
  1519.   Cell: TGridCoord;
  1520.   R: TRect;
  1521. begin
  1522.   MasterCol := nil;
  1523.   Result := False;
  1524.   Cell := MouseCoord(X,Y);
  1525.   if (Cell.Y < FTitleOffset) and FDatalink.Active
  1526.     and (Cell.X >= FIndicatorOffset)
  1527.     and (RawToDataColumn(Cell.X) < Columns.Count) then
  1528.   begin
  1529.     R := CalcTitleRect(Columns[RawToDataColumn(Cell.X)], Cell.Y, MasterCol);
  1530.     if not UseRightToLeftAlignment then
  1531.       R.Left := R.Right - GetSystemMetrics(SM_CXHSCROLL)
  1532.     else
  1533.       R.Right := R.Left + GetSystemMetrics(SM_CXHSCROLL);
  1534.     Result := MasterCol.Expandable and PtInRect(R, Point(X,Y));
  1535.   end;
  1536. end;
  1537. procedure TbsSkinCustomDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState;
  1538.   X, Y: Integer);
  1539. var
  1540.   Cell: TGridCoord;
  1541.   OldCol,OldRow: Integer;
  1542.   MasterCol: TbsColumn;
  1543. begin
  1544.   if not AcquireFocus then Exit;
  1545.   if (ssDouble in Shift) and (Button = mbLeft) then
  1546.   begin
  1547.     DblClick;
  1548.     Exit;
  1549.   end;
  1550.   if Sizing(X, Y) then
  1551.   begin
  1552.     FDatalink.UpdateData;
  1553.     inherited MouseDown(Button, Shift, X, Y);
  1554.     Exit;
  1555.   end;
  1556.   Cell := MouseCoord(X, Y);
  1557.   if (Cell.X < 0) and (Cell.Y < 0) then
  1558.   begin
  1559.     inherited MouseDown(Button, Shift, X, Y);
  1560.     Exit;
  1561.   end;
  1562.   if (DragKind = dkDock) and (Cell.X < FIndicatorOffset) and
  1563.     (Cell.Y < FTitleOffset) and (not (csDesigning in ComponentState)) then
  1564.   begin
  1565.     BeginDrag(false);
  1566.     Exit;
  1567.   end;
  1568.   if PtInExpandButton(X,Y, MasterCol) then
  1569.   begin
  1570.     MasterCol.Expanded := not MasterCol.Expanded;
  1571.     ReleaseCapture;
  1572.     UpdateDesigner;
  1573.     Exit;
  1574.   end;
  1575.   if ((csDesigning in ComponentState) or (dgColumnResize in Options)) and
  1576.     (Cell.Y < FTitleOffset) then
  1577.   begin
  1578.     FDataLink.UpdateData;
  1579.     inherited MouseDown(Button, Shift, X, Y);
  1580.     Exit;
  1581.   end;
  1582.   if FDatalink.Active then
  1583.     with Cell do
  1584.     begin
  1585.       BeginUpdate;   { eliminates highlight flicker when selection moves }
  1586.       try
  1587.         FDatalink.UpdateData; // validate before moving
  1588.         HideEditor;
  1589.         OldCol := Col;
  1590.         OldRow := Row;
  1591.         if (Y >= FTitleOffset) and (Y - Row <> 0) then
  1592.           FDatalink.MoveBy(Y - Row);
  1593.         if X >= FIndicatorOffset then
  1594.           MoveCol(X, 0);
  1595.         if (dgMultiSelect in Options) and FDatalink.Active then
  1596.           with FBookmarks do
  1597.           begin
  1598.             FSelecting := False;
  1599.             if ssCtrl in Shift then
  1600.               CurrentRowSelected := not CurrentRowSelected
  1601.             else
  1602.             begin
  1603.               Clear;
  1604.               CurrentRowSelected := True;
  1605.             end;
  1606.           end;
  1607.         if (Button = mbLeft) and
  1608.           (((X = OldCol) and (Y = OldRow)) or (dgAlwaysShowEditor in Options)) then
  1609.           ShowEditor         { put grid in edit mode }
  1610.         else
  1611.           InvalidateEditor;  { draw editor, if needed }
  1612.       finally
  1613.         EndUpdate;
  1614.       end;
  1615.     end;
  1616. end;
  1617. procedure TbsSkinCustomDBGrid.MouseUp(Button: TMouseButton; Shift: TShiftState;
  1618.   X, Y: Integer);
  1619. var
  1620.   Cell: TGridCoord;
  1621.   SaveState: TbsGridState;
  1622. begin
  1623.   SaveState := FGridState;
  1624.   inherited MouseUp(Button, Shift, X, Y);
  1625.   if (SaveState = gsRowSizing) or (SaveState = gsColSizing) or
  1626.     ((InplaceEditor <> nil) and (InplaceEditor.Visible) and
  1627.      (PtInRect(InplaceEditor.BoundsRect, Point(X,Y)))) then Exit;
  1628.   Cell := MouseCoord(X,Y);
  1629.   if (Button = mbLeft) and (Cell.X >= FIndicatorOffset) and (Cell.Y >= 0) then
  1630.     if Cell.Y < FTitleOffset then
  1631.       TitleClick(Columns[RawToDataColumn(Cell.X)])
  1632.     else
  1633.       CellClick(Columns[SelectedIndex]);
  1634. end;
  1635. procedure TbsSkinCustomDBGrid.MoveCol(RawCol, Direction: Integer);
  1636. var
  1637.   OldCol: Integer;
  1638. begin
  1639.   FDatalink.UpdateData;
  1640.   if RawCol >= ColCount then
  1641.     RawCol := ColCount - 1;
  1642.   if RawCol < FIndicatorOffset then RawCol := FIndicatorOffset;
  1643.   if Direction <> 0 then
  1644.   begin
  1645.     while (RawCol < ColCount) and (RawCol >= FIndicatorOffset) and
  1646.       (ColWidths[RawCol] <= 0) do
  1647.       Inc(RawCol, Direction);
  1648.     if (RawCol >= ColCount) or (RawCol < FIndicatorOffset) then Exit;
  1649.   end;
  1650.   OldCol := Col;
  1651.   if RawCol <> OldCol then
  1652.   begin
  1653.     if not FInColExit then
  1654.     begin
  1655.       FInColExit := True;
  1656.       try
  1657.         ColExit;
  1658.       finally
  1659.         FInColExit := False;
  1660.       end;
  1661.       if Col <> OldCol then Exit;
  1662.     end;
  1663.     if not (dgAlwaysShowEditor in Options) then HideEditor;
  1664.     Col := RawCol;
  1665.     ColEnter;
  1666.   end;
  1667. end;
  1668. procedure TbsSkinCustomDBGrid.Notification(AComponent: TComponent;
  1669.   Operation: TOperation);
  1670. var
  1671.   I: Integer;
  1672.   NeedLayout: Boolean;
  1673. begin
  1674.   inherited Notification(AComponent, Operation);
  1675.   if (Operation = opRemove) and (FSkinMessage <> nil) and
  1676.     (AComponent = FSkinMessage) then FSkinMessage := nil;
  1677.   if (Operation = opRemove) then
  1678.   begin
  1679.     if (AComponent is TPopupMenu) then
  1680.     begin
  1681.       for I := 0 to Columns.Count-1 do
  1682.         if Columns[I].PopupMenu = AComponent then
  1683.           Columns[I].PopupMenu := nil;
  1684.     end
  1685.     else if (FDataLink <> nil) then
  1686.       if (AComponent = DataSource)  then
  1687.         DataSource := nil
  1688.       else if (AComponent is TField) then
  1689.       begin
  1690.         NeedLayout := False;
  1691.         BeginLayout;
  1692.         try
  1693.           for I := 0 to Columns.Count-1 do
  1694.             with Columns[I] do
  1695.               if Field = AComponent then
  1696.               begin
  1697.                 Field := nil;
  1698.                 NeedLayout := True;
  1699.               end;
  1700.         finally
  1701.           if NeedLayout and Assigned(FDatalink.Dataset)
  1702.             and not FDatalink.Dataset.ControlsDisabled then
  1703.             EndLayout
  1704.           else
  1705.             DeferLayout;
  1706.         end;
  1707.       end;
  1708.   end;
  1709. end;
  1710. procedure TbsSkinCustomDBGrid.RecordChanged(Field: TField);
  1711. var
  1712.   I: Integer;
  1713.   CField: TField;
  1714. begin
  1715.   if not HandleAllocated then Exit;
  1716.   if Field = nil then
  1717.     Invalidate
  1718.   else
  1719.   begin
  1720.     for I := 0 to Columns.Count - 1 do
  1721.       if Columns[I].Field = Field then
  1722.         InvalidateCol(DataToRawColumn(I));
  1723.   end;
  1724.   CField := SelectedField;
  1725.   if ((Field = nil) or (CField = Field)) and
  1726.     (Assigned(CField) and (CField.Text <> FEditText) and
  1727.     ((SysLocale.PriLangID <> LANG_KOREAN) or FIsESCKey)) then
  1728.   begin
  1729.     InvalidateEditor;
  1730.     if InplaceEditor <> nil then InplaceEditor.Deselect;
  1731.   end;
  1732. end;
  1733. procedure TbsSkinCustomDBGrid.Scroll(Distance: Integer);
  1734. var
  1735.   OldRect, NewRect: TRect;
  1736.   RowHeight: Integer;
  1737. begin
  1738.   if not HandleAllocated then Exit;
  1739.   OldRect := BoxRect(0, Row, ColCount - 1, Row);
  1740.   if (FDataLink.ActiveRecord >= RowCount - FTitleOffset) then UpdateRowCount;
  1741.   UpdateScrollBar;
  1742.   UpdateActive;
  1743.   NewRect := BoxRect(0, Row, ColCount - 1, Row);
  1744.   ValidateRect(Handle, @OldRect);
  1745.   InvalidateRect(Handle, @OldRect, False);
  1746.   InvalidateRect(Handle, @NewRect, False);
  1747.   if Distance <> 0 then
  1748.   begin
  1749.     HideEditor;
  1750.     try
  1751.       if Abs(Distance) > VisibleRowCount then
  1752.       begin
  1753.         Invalidate;
  1754.         Exit;
  1755.       end
  1756.       else
  1757.       begin
  1758.         RowHeight := DefaultRowHeight;
  1759.         if dgRowLines in Options then Inc(RowHeight, GridLineWidth);
  1760.         if dgIndicator in Options then
  1761.         begin
  1762.           OldRect := BoxRect(0, FSelRow, ColCount - 1, FSelRow);
  1763.           InvalidateRect(Handle, @OldRect, False);
  1764.         end;
  1765.         NewRect := BoxRect(0, FTitleOffset, ColCount - 1, 1000);
  1766.         ScrollWindowEx(Handle, 0, -RowHeight * Distance, @NewRect, @NewRect,
  1767.           0, nil, SW_Invalidate);
  1768.         if dgIndicator in Options then
  1769.         begin
  1770.           NewRect := BoxRect(0, Row, ColCount - 1, Row);
  1771.           InvalidateRect(Handle, @NewRect, False);
  1772.         end;
  1773.       end;
  1774.     finally
  1775.       if dgAlwaysShowEditor in Options then ShowEditor;
  1776.     end;
  1777.   end;
  1778.   if UpdateLock = 0 then Update;
  1779. end;
  1780. procedure TbsSkinCustomDBGrid.SetColumns(Value: TbsDBGridColumns);
  1781. begin
  1782.   Columns.Assign(Value);
  1783. end;
  1784. function ReadOnlyField(Field: TField): Boolean;
  1785. var
  1786.   MasterField: TField;
  1787. begin
  1788.   Result := Field.ReadOnly;
  1789.   if not Result and (Field.FieldKind = fkLookup) then
  1790.   begin
  1791.     Result := True;
  1792.     if Field.DataSet = nil then Exit;
  1793.     MasterField := Field.Dataset.FindField(Field.KeyFields);
  1794.     if MasterField = nil then Exit;
  1795.     Result := MasterField.ReadOnly;
  1796.   end;
  1797. end;
  1798. procedure TbsSkinCustomDBGrid.SetColumnAttributes;
  1799. var
  1800.   I: Integer;
  1801. begin
  1802.   for I := 0 to FColumns.Count-1 do
  1803.   with FColumns[I] do
  1804.   begin
  1805.     TabStops[I + FIndicatorOffset] := Showing and not ReadOnly and DataLink.Active and
  1806.       Assigned(Field) and not (Field.FieldKind = fkCalculated) and not ReadOnlyField(Field);
  1807.     ColWidths[I + FIndicatorOffset] := Width;
  1808.   end;
  1809.   if (dgIndicator in Options) then
  1810.   if FIndex = -1
  1811.   then
  1812.     ColWidths[0] := IndicatorWidth
  1813.   else
  1814.     if FixedCellLeftOffset + FixedCellRightOffset >= IndicatorWidth
  1815.     then
  1816.       ColWidths[0] := FixedCellLeftOffset + FixedCellRightOffset
  1817.     else
  1818.       ColWidths[0] := IndicatorWidth;
  1819. end;
  1820. procedure TbsSkinCustomDBGrid.SetDataSource(Value: TDataSource);
  1821. begin
  1822.   if Value = FDatalink.Datasource then Exit;
  1823.   FBookmarks.Clear;
  1824.   FDataLink.DataSource := Value;
  1825.   if Value <> nil then Value.FreeNotification(Self);
  1826. end;
  1827. procedure TbsSkinCustomDBGrid.SetEditText(ACol, ARow: Longint; const Value: string);
  1828. begin
  1829.   FEditText := Value;
  1830. end;
  1831. procedure TbsSkinCustomDBGrid.SetOptions(Value: TbsDBGridOptions);
  1832. const
  1833.   LayoutOptions = [dgEditing, dgAlwaysShowEditor, dgTitles, dgIndicator,
  1834.     dgColLines, dgRowLines, dgRowSelect, dgAlwaysShowSelection];
  1835. var
  1836.   NewGridOptions: TGridOptions;
  1837.   ChangedOptions: TbsDBGridOptions;
  1838. begin
  1839.   if FOptions <> Value then
  1840.   begin
  1841.     NewGridOptions := [];
  1842.     if dgColLines in Value then
  1843.       NewGridOptions := NewGridOptions + [goFixedVertLine, goVertLine];
  1844.     if dgRowLines in Value then
  1845.       NewGridOptions := NewGridOptions + [goFixedHorzLine, goHorzLine];
  1846.     if dgColumnResize in Value then
  1847.       NewGridOptions := NewGridOptions + [goColSizing, goColMoving];
  1848.     if dgTabs in Value then Include(NewGridOptions, goTabs);
  1849.     if dgRowSelect in Value then
  1850.     begin
  1851.       Include(NewGridOptions, goRowSelect);
  1852.       Exclude(Value, dgAlwaysShowEditor);
  1853.       Exclude(Value, dgEditing);
  1854.     end;
  1855.     if dgEditing in Value then Include(NewGridOptions, goEditing);
  1856.     if dgAlwaysShowEditor in Value then Include(NewGridOptions, goAlwaysShowEditor);
  1857.     inherited Options := NewGridOptions;
  1858.     if dgMultiSelect in (FOptions - Value) then FBookmarks.Clear;
  1859.     ChangedOptions := (FOptions + Value) - (FOptions * Value);
  1860.     FOptions := Value;
  1861.     if ChangedOptions * LayoutOptions <> [] then LayoutChanged;
  1862.   end;
  1863. end;
  1864. procedure TbsSkinCustomDBGrid.SetSelectedField(Value: TField);
  1865. var
  1866.   I: Integer;
  1867. begin
  1868.   if Value = nil then Exit;
  1869.   for I := 0 to Columns.Count - 1 do
  1870.     if Columns[I].Field = Value then
  1871.       MoveCol(DataToRawColumn(I), 0);
  1872. end;
  1873. procedure TbsSkinCustomDBGrid.SetSelectedIndex(Value: Integer);
  1874. begin
  1875.   MoveCol(DataToRawColumn(Value), 0);
  1876. end;
  1877. procedure TbsSkinCustomDBGrid.SetTitleFont(Value: TFont);
  1878. begin
  1879.   FTitleFont.Assign(Value);
  1880.   if dgTitles in Options then LayoutChanged;
  1881. end;
  1882. function TbsSkinCustomDBGrid.StoreColumns: Boolean;
  1883. begin
  1884.   Result := Columns.State = csCustomized;
  1885. end;
  1886. procedure TbsSkinCustomDBGrid.TimedScroll(Direction: TGridScrollDirection);
  1887. begin
  1888.   if FDatalink.Active then
  1889.   begin
  1890.     with FDatalink do
  1891.     begin
  1892.       if sdUp in Direction then
  1893.       begin
  1894.         FDataLink.MoveBy(-ActiveRecord - 1);
  1895.         Exclude(Direction, sdUp);
  1896.       end;
  1897.       if sdDown in Direction then
  1898.       begin
  1899.         FDataLink.MoveBy(RecordCount - ActiveRecord);
  1900.         Exclude(Direction, sdDown);
  1901.       end;
  1902.     end;
  1903.     if Direction <> [] then inherited TimedScroll(Direction);
  1904.   end;
  1905. end;
  1906. procedure TbsSkinCustomDBGrid.TitleClick(Column: TbsColumn);
  1907. begin
  1908.   if Assigned(FOnTitleClick) then FOnTitleClick(Column);
  1909. end;
  1910. procedure TbsSkinCustomDBGrid.TitleFontChanged(Sender: TObject);
  1911. begin
  1912.   if (not FSelfChangingTitleFont) and not (csLoading in ComponentState) then
  1913.     ParentFont := False;
  1914.   if dgTitles in Options then LayoutChanged;
  1915. end;
  1916. procedure TbsSkinCustomDBGrid.UpdateActive;
  1917. var
  1918.   NewRow: Integer;
  1919.   Field: TField;
  1920. begin
  1921.   if FDatalink.Active and HandleAllocated and not (csLoading in ComponentState) then
  1922.   begin
  1923.     NewRow := FDatalink.ActiveRecord + FTitleOffset;
  1924.     if Row <> NewRow then
  1925.     begin
  1926.       if not (dgAlwaysShowEditor in Options) then HideEditor;
  1927.       MoveColRow(Col, NewRow, False, False);
  1928.       InvalidateEditor;
  1929.     end;
  1930.     Field := SelectedField;
  1931.     if Assigned(Field) and (Field.Text <> FEditText) then
  1932.       InvalidateEditor;
  1933.   end;
  1934. end;
  1935. procedure TbsSkinCustomDBGrid.UpdateData;
  1936. var
  1937.   Field: TField;
  1938. begin
  1939.   Field := SelectedField;
  1940.   if Assigned(Field) then
  1941.     Field.Text := FEditText;
  1942. end;
  1943. procedure TbsSkinCustomDBGrid.UpdateRowCount;
  1944. var
  1945.   OldRowCount: Integer;
  1946. begin
  1947.   OldRowCount := RowCount;
  1948.   if RowCount <= FTitleOffset then RowCount := FTitleOffset + 1;
  1949.   FixedRows := FTitleOffset;
  1950.   with FDataLink do
  1951.     if not Active or (RecordCount = 0) or not HandleAllocated then
  1952.       RowCount := 1 + FTitleOffset
  1953.     else
  1954.     begin
  1955.       RowCount := 1000;
  1956.       FDataLink.BufferCount := VisibleRowCount;
  1957.       RowCount := RecordCount + FTitleOffset;
  1958.       if dgRowSelect in Options then TopRow := FixedRows;
  1959.       UpdateActive;
  1960.     end;
  1961.   if OldRowCount <> RowCount then Invalidate;
  1962. end;
  1963. type
  1964.   TParentControl = class(TWinControl);
  1965. procedure TbsSkinCustomDBGrid.UpdateScrollBar;
  1966. var
  1967.   Pos: Integer;
  1968.   OldVisible, VVisible, VVisibleChanged: Boolean;
  1969.   R: TRect;
  1970. begin
  1971.   VVisibleChanged := False;
  1972.   if FDatalink.Active and HandleAllocated then
  1973.     with FDatalink.DataSet do
  1974.     begin
  1975.       if (VScrollBar <> nil)
  1976.       then
  1977.         begin
  1978.           OldVisible := VScrollBar.Visible;
  1979.           VVisible := Self.RowCount >= Self.VisibleRowCount;
  1980.           VVisibleChanged := OldVisible <> VVisible;
  1981.           if IsSequenced
  1982.           then
  1983.             begin
  1984.               if RecNo <> -1
  1985.               then
  1986.                 VScrollBar.SetRange(1, Integer(DWORD(RecordCount)) + Self.VisibleRowCount - 1,
  1987.                   RecNo, Self.VisibleRowCount);
  1988.             end
  1989.           else
  1990.             begin
  1991.               if FDataLink.BOF then Pos := 0
  1992.               else if FDataLink.EOF then Pos := 4
  1993.               else Pos := 2;
  1994.               VScrollBar.SetRange(0, 4, Pos, 0);
  1995.             end;
  1996.         end;
  1997.     end
  1998.   else
  1999.     if (VScrollBar <>  nil) and VScrollBar.Visible
  2000.     then
  2001.       begin
  2002.         VVisible := False;
  2003.         VVisibleChanged := True;
  2004.       end;
  2005.   FInCheckScrollBars := True;
  2006.   if VVisibleChanged then VScrollBar.Visible := VVisible;
  2007.   FInCheckScrollBars := False;
  2008.    if (VScrollBar <> nil) and (HScrollBar <> nil)
  2009.   then
  2010.     begin
  2011.       if not VScrollBar.Visible and HScrollBar.Both
  2012.       then
  2013.         HScrollBar.Both := False
  2014.       else
  2015.         if VScrollBar.Visible and not HScrollBar.Both
  2016.         then
  2017.           HScrollBar.Both := True;
  2018.     end;
  2019.   if (Self.Align <> alNone) and VVisibleChanged
  2020.   then
  2021.     begin
  2022.       R := Parent.ClientRect;
  2023.       TParentControl(Parent).AlignControls(nil, R);
  2024.       FInCheckScrollBars := True;
  2025.       Invalidate;
  2026.       FInCheckScrollBars := False;
  2027.     end;
  2028. end;
  2029. function TbsSkinCustomDBGrid.ValidFieldIndex(FieldIndex: Integer): Boolean;
  2030. begin
  2031.   Result := DataLink.GetMappedIndex(FieldIndex) >= 0;
  2032. end;
  2033. procedure TbsSkinCustomDBGrid.CMParentFontChanged(var Message: TMessage);
  2034. begin
  2035.   inherited;
  2036.   if ParentFont then
  2037.   begin
  2038.     FSelfChangingTitleFont := True;
  2039.     try
  2040.       TitleFont := Font;
  2041.     finally
  2042.       FSelfChangingTitleFont := False;
  2043.     end;
  2044.     LayoutChanged;
  2045.   end;
  2046. end;
  2047. procedure TbsSkinCustomDBGrid.CMBiDiModeChanged(var Message: TMessage);
  2048. var
  2049.   Loop: Integer;
  2050. begin
  2051.   inherited;
  2052.   for Loop := 0 to ComponentCount - 1 do
  2053.     if Components[Loop] is TbsSkinCustomDBGrid then
  2054.       with Components[Loop] as TbsSkinCustomDBGrid do
  2055.         { Changing the window, echos down to the subgrid }
  2056.         if Parent <> nil then
  2057.           Parent.BiDiMode := Self.BiDiMode;
  2058. end;
  2059. procedure TbsSkinCustomDBGrid.CMExit(var Message: TMessage);
  2060. begin
  2061.   try
  2062.     if FDatalink.Active then
  2063.       with FDatalink.Dataset do
  2064.         if (dgCancelOnExit in Options) and (State = dsInsert) and
  2065.           not Modified and not FDatalink.FModified then
  2066.           Cancel else
  2067.           FDataLink.UpdateData;
  2068.   except
  2069.     SetFocus;
  2070.     raise;
  2071.   end;
  2072.   inherited;
  2073. end;
  2074. procedure TbsSkinCustomDBGrid.CMFontChanged(var Message: TMessage);
  2075. var
  2076.   I: Integer;
  2077. begin
  2078.   inherited;
  2079.   BeginLayout;
  2080.   try
  2081.     for I := 0 to Columns.Count-1 do
  2082.       Columns[I].RefreshDefaultFont;
  2083.   finally
  2084.     EndLayout;
  2085.   end;
  2086. end;
  2087. procedure TbsSkinCustomDBGrid.CMDeferLayout(var Message);
  2088. begin
  2089.   if AcquireLayoutLock then
  2090.     EndLayout
  2091.   else
  2092.     DeferLayout;
  2093. end;
  2094. procedure TbsSkinCustomDBGrid.CMDesignHitTest(var Msg: TCMDesignHitTest);
  2095. var
  2096.   MasterCol: TbsColumn;
  2097. begin
  2098.   inherited;
  2099.   if (Msg.Result = 1) and ((FDataLink = nil) or
  2100.     ((Columns.State = csDefault) and
  2101.      (FDataLink.DefaultFields or (not FDataLink.Active)))) then
  2102.     Msg.Result := 0
  2103.   else if (Msg.Result = 0) and (FDataLink <> nil) and (FDataLink.Active)
  2104.     and (Columns.State = csCustomized)
  2105.     and PtInExpandButton(Msg.XPos, Msg.YPos, MasterCol) then
  2106.     Msg.Result := 1;
  2107. end;
  2108. procedure TbsSkinCustomDBGrid.WMSetCursor(var Msg: TWMSetCursor);
  2109. begin
  2110.   if (csDesigning in ComponentState) and
  2111.       ((FDataLink = nil) or
  2112.        ((Columns.State = csDefault) and
  2113.         (FDataLink.DefaultFields or not FDataLink.Active))) then
  2114.     Windows.SetCursor(LoadCursor(0, IDC_ARROW))
  2115.   else inherited;
  2116. end;
  2117. procedure TbsSkinCustomDBGrid.WMSize(var Message: TWMSize);
  2118. begin
  2119.   inherited;
  2120.   if UpdateLock = 0 then UpdateRowCount;
  2121.   InvalidateTitles;
  2122. end;
  2123. procedure TbsSkinCustomDBGrid.WMVScroll(var Message: TWMVScroll);
  2124. begin
  2125.   if not AcquireFocus then Exit;
  2126.   if FDatalink.Active then
  2127.     with Message, FDataLink.DataSet do
  2128.       case ScrollCode of
  2129.         SB_LINEUP: FDataLink.MoveBy(-FDatalink.ActiveRecord - 1);
  2130.         SB_LINEDOWN: FDataLink.MoveBy(FDatalink.RecordCount - FDatalink.ActiveRecord);
  2131.         SB_PAGEUP: FDataLink.MoveBy(-VisibleRowCount);
  2132.         SB_PAGEDOWN: FDataLink.MoveBy(VisibleRowCount);
  2133.         SB_THUMBPOSITION:
  2134.           if (VScrollBar <> nil)
  2135.           then
  2136.             with VScrollBar do
  2137.             begin
  2138.               if IsSequenced
  2139.               then
  2140.                 begin
  2141.                   if Position <= 1 then First
  2142.                   else if Position >= RecordCount then Last
  2143.                   else RecNo := Position;
  2144.                 end
  2145.               else
  2146.                 case Position of
  2147.                   0: First;
  2148.                   1: FDataLink.MoveBy(-VisibleRowCount);
  2149.                   2: Exit;
  2150.                   3: FDataLink.MoveBy(VisibleRowCount);
  2151.                   4: Last;
  2152.                 end;
  2153.             end;
  2154.         SB_BOTTOM: Last;
  2155.         SB_TOP: First;
  2156.       end;
  2157. end;
  2158. procedure TbsSkinCustomDBGrid.SetIme;
  2159. var
  2160.   Column: TbsColumn;
  2161. begin
  2162.   if not SysLocale.FarEast then Exit;
  2163.   if Columns.Count = 0 then Exit;
  2164.   ImeName := FOriginalImeName;
  2165.   ImeMode := FOriginalImeMode;
  2166.   Column := Columns[SelectedIndex];
  2167.   if Column.IsImeNameStored then ImeName := Column.ImeName;
  2168.   if Column.IsImeModeStored then ImeMode := Column.ImeMode;
  2169.   if InplaceEditor <> nil then
  2170.   begin
  2171.     TDBGridInplaceEdit(Self).ImeName := ImeName;
  2172.     TDBGridInplaceEdit(Self).ImeMode := ImeMode;
  2173.   end;
  2174. end;
  2175. procedure TbsSkinCustomDBGrid.UpdateIme;
  2176. begin
  2177.   if not SysLocale.FarEast then Exit;
  2178.   SetIme;
  2179.   SetImeName(ImeName);
  2180.   SetImeMode(Handle, ImeMode);
  2181. end;
  2182. procedure TbsSkinCustomDBGrid.WMIMEStartComp(var Message: TMessage);
  2183. begin
  2184.   inherited;
  2185.   ShowEditor;
  2186. end;
  2187. procedure TbsSkinCustomDBGrid.WMSetFocus(var Message: TWMSetFocus);
  2188. begin
  2189.   if not ((InplaceEditor <> nil) and
  2190.     (Message.FocusedWnd = InplaceEditor.Handle)) then SetIme;
  2191.   inherited;
  2192. end;
  2193. procedure TbsSkinCustomDBGrid.WMKillFocus(var Message: TMessage);
  2194. begin
  2195.   if not SysLocale.FarEast then inherited
  2196.   else
  2197.   begin
  2198.     ImeName := Screen.DefaultIme;
  2199.     ImeMode := imDontCare;
  2200.     inherited;
  2201.     if not ((InplaceEditor <> nil) and
  2202.       (HWND(Message.WParam) = InplaceEditor.Handle)) then
  2203.       ActivateKeyboardLayout(Screen.DefaultKbLayout, KLF_ACTIVATE);
  2204.   end;
  2205. end;
  2206. { Defer action processing to datalink }
  2207. function TbsSkinCustomDBGrid.ExecuteAction(Action: TBasicAction): Boolean;
  2208. begin
  2209.   Result := (DataLink <> nil) and DataLink.ExecuteAction(Action);
  2210. end;
  2211. function TbsSkinCustomDBGrid.UpdateAction(Action: TBasicAction): Boolean;
  2212. begin
  2213.   Result := (DataLink <> nil) and DataLink.UpdateAction(Action);
  2214. end;
  2215. procedure TbsSkinCustomDBGrid.ShowPopupEditor(Column: TbsColumn; X, Y: Integer);
  2216. var
  2217.   SubGrid: TbsSkinCustomDBGrid;
  2218.   DS: TDataSource;
  2219.   I: Integer;
  2220.   FloatRect: TRect;
  2221.   Cmp: TControl;
  2222. begin
  2223.   if not ((Column.Field <> nil) and (Column.Field is TDataSetField)) then  Exit;
  2224.   // find existing popup for this column field, if any, and show it
  2225.   for I := 0 to ComponentCount-1 do
  2226.     if Components[I] is TbsSkinCustomDBGrid then
  2227.     begin
  2228.       SubGrid := TbsSkinCustomDBGrid(Components[I]);
  2229.       if (SubGrid.DataSource <> nil) and
  2230.         (SubGrid.DataSource.DataSet = (Column.Field as TDatasetField).NestedDataset) and
  2231.         SubGrid.CanFocus then
  2232.       begin
  2233.         SubGrid.Parent.Show;
  2234.         SubGrid.SetFocus;
  2235.         Exit;
  2236.       end;
  2237.     end;
  2238.   // create another instance of this kind of grid
  2239.   SubGrid := TbsSkinCustomDBGrid(TComponentClass(Self.ClassType).Create(Self));
  2240.   try
  2241.     DS := TDataSource.Create(SubGrid); // incestuous, but easy cleanup
  2242.     DS.Dataset := (Column.Field as TDatasetField).NestedDataset;
  2243.     DS.DataSet.CheckBrowseMode;
  2244.     SubGrid.DataSource := DS;
  2245.     SubGrid.Columns.State := Columns.State;
  2246.     SubGrid.Columns[0].Expanded := True;
  2247.     SubGrid.Visible := False;
  2248.     SubGrid.FloatingDockSiteClass := TCustomDockForm;
  2249.     FloatRect.TopLeft := ClientToScreen(CellRect(Col, Row).BottomRight);
  2250.     if X > Low(Integer) then FloatRect.Left := X;
  2251.     if Y > Low(Integer) then FloatRect.Top := Y;
  2252.     FloatRect.Right := FloatRect.Left + Width;
  2253.     FloatRect.Bottom := FloatRect.Top + Height;
  2254.     SubGrid.ManualFloat(FloatRect);
  2255. //    SubGrid.ManualDock(nil,nil,alClient);
  2256.     SubGrid.Parent.BiDiMode := Self.BiDiMode; { This carries the BiDi setting }
  2257.     I := SubGrid.CellRect(SubGrid.ColCount-1, 0).Right;
  2258.     if (I > 0) and (I < Screen.Width div 2) then
  2259.       SubGrid.Parent.ClientWidth := I
  2260.     else
  2261.       SubGrid.Parent.Width := Screen.Width div 4;
  2262.     SubGrid.Parent.Height := Screen.Height div 4;
  2263.     SubGrid.Align := alClient;
  2264.     SubGrid.DragKind := dkDock;
  2265.     SubGrid.Color := Color;
  2266.     SubGrid.Ctl3D := Ctl3D;
  2267.     SubGrid.Cursor := Cursor;
  2268.     SubGrid.Enabled := Enabled;
  2269.     SubGrid.FixedColor := FixedColor;
  2270.     SubGrid.Font := Font;
  2271.     SubGrid.HelpContext := HelpContext;
  2272.     SubGrid.IMEMode := IMEMode;
  2273.     SubGrid.IMEName := IMEName;
  2274.     SubGrid.Options := Options;
  2275.     Cmp := Self;
  2276.     while (Cmp <> nil) and (TbsSkinCustomDBGrid(Cmp).PopupMenu = nil) do
  2277.       Cmp := Cmp.Parent;
  2278.     if Cmp <> nil then
  2279.       SubGrid.PopupMenu := TbsSkinCustomDBGrid(Cmp).PopupMenu;
  2280.     SubGrid.TitleFont := TitleFont;
  2281.     SubGrid.Visible := True;
  2282.     SubGrid.Parent.Show;
  2283.   except
  2284.     SubGrid.Free;
  2285.     raise;
  2286.   end;
  2287. end;
  2288. procedure TbsSkinCustomDBGrid.CalcSizingState(X, Y: Integer;
  2289.   var State: TbsGridState; var Index, SizingPos, SizingOfs: Integer;
  2290.   var FixedInfo: TbsGridDrawInfo);
  2291. var
  2292.   R: TGridCoord;
  2293. begin
  2294.   inherited CalcSizingState(X, Y, State, Index, SizingPos, SizingOfs, FixedInfo);
  2295.   if (State = gsColSizing) and (FDataLink <> nil)
  2296.     and (FDatalink.Dataset <> nil) and FDataLink.Dataset.ObjectView then
  2297.   begin
  2298.     R := MouseCoord(X, Y);
  2299.     R.X := RawToDataColumn(R.X);
  2300.     if (R.X >= 0) and (R.X < Columns.Count) and (Columns[R.X].Depth > R.Y) then
  2301.       State := gsNormal;
  2302.   end;
  2303. end;
  2304. function TbsSkinCustomDBGrid.CheckColumnDrag(var Origin, Destination: Integer;
  2305.   const MousePt: TPoint): Boolean;
  2306. var
  2307.   I, ARow: Integer;
  2308.   DestCol: TbsColumn;
  2309. begin
  2310.   Result := inherited CheckColumnDrag(Origin, Destination, MousePt);
  2311.   if Result and (FDatalink.Dataset <> nil) and FDatalink.Dataset.ObjectView then
  2312.   begin
  2313.     assert(FDragCol <> nil);
  2314.     ARow := FDragCol.Depth;
  2315.     if Destination <> Origin then
  2316.     begin
  2317.       DestCol := ColumnAtDepth(Columns[RawToDataColumn(Destination)], ARow);
  2318.       if DestCol.ParentColumn <> FDragCol.ParentColumn then
  2319.         if Destination < Origin then
  2320.           DestCol := Columns[FDragCol.ParentColumn.Index+1]
  2321.         else
  2322.         begin
  2323.           I := DestCol.Index;
  2324.           while DestCol.ParentColumn <> FDragCol.ParentColumn do
  2325.           begin
  2326.             Dec(I);
  2327.             DestCol := Columns[I];
  2328.           end;
  2329.         end;
  2330.       if (DestCol.Index > FDragCol.Index) then
  2331.       begin
  2332.         I := DestCol.Index + 1;
  2333.         while (I < Columns.Count) and (ColumnAtDepth(Columns[I],ARow) = DestCol) do
  2334.           Inc(I);
  2335.         DestCol := Columns[I-1];
  2336.       end;
  2337.       Destination := DataToRawColumn(DestCol.Index);
  2338.     end;
  2339.   end;
  2340. end;
  2341. function TbsSkinCustomDBGrid.BeginColumnDrag(var Origin, Destination: Integer;
  2342.   const MousePt: TPoint): Boolean;
  2343. var
  2344.   I, ARow: Integer;
  2345. begin
  2346.   Result := inherited BeginColumnDrag(Origin, Destination, MousePt);
  2347.   if Result and (FDatalink.Dataset <> nil) and FDatalink.Dataset.ObjectView then
  2348.   begin
  2349.     ARow := MouseCoord(MousePt.X, MousePt.Y).Y;
  2350.     FDragCol := ColumnAtDepth(Columns[RawToDataColumn(Origin)], ARow);
  2351.     if FDragCol = nil then Exit;
  2352.     I := DataToRawColumn(FDragCol.Index);
  2353.     if Origin <> I then Origin := I;
  2354.     Destination := Origin;
  2355.   end;
  2356. end;
  2357. function TbsSkinCustomDBGrid.EndColumnDrag(var Origin, Destination: Integer;
  2358.   const MousePt: TPoint): Boolean;
  2359. begin
  2360.   Result := inherited EndColumnDrag(Origin, Destination, MousePt);
  2361.   FDragCol := nil;
  2362. end;
  2363. procedure TbsSkinCustomDBGrid.InvalidateTitles;
  2364. var
  2365.   R: TRect;
  2366.   DrawInfo: TbsGridDrawInfo;
  2367. begin
  2368.   if HandleAllocated and (dgTitles in Options) then
  2369.   begin
  2370.     CalcFixedInfo(DrawInfo);
  2371.     R := Rect(0, 0, Width, DrawInfo.Vert.FixedBoundary);
  2372.     InvalidateRect(Handle, @R, False);
  2373.   end;
  2374. end;
  2375. procedure TbsSkinCustomDBGrid.TopLeftChanged;
  2376. begin
  2377.   InvalidateTitles; 
  2378.   inherited TopLeftChanged;
  2379. end;
  2380. end.