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

Delphi控件源码

开发平台:

Delphi

  1. end;
  2. procedure TbsSkinCustomGrid.ModifyScrollBar(ScrollBar, ScrollCode, Pos: Cardinal;
  3.   UseRightToLeft: Boolean);
  4. var
  5.   NewTopLeft, MaxTopLeft: TGridCoord;
  6.   DrawInfo: TbsGridDrawInfo;
  7.   RTLFactor: Integer;
  8.   function Min: Longint;
  9.   begin
  10.     if ScrollBar = SB_HORZ then Result := FixedCols
  11.     else Result := FixedRows;
  12.   end;
  13.   function Max: Longint;
  14.   begin
  15.     if ScrollBar = SB_HORZ then Result := MaxTopLeft.X
  16.     else Result := MaxTopLeft.Y;
  17.   end;
  18.   function PageUp: Longint;
  19.   var
  20.     MaxTopLeft: TGridCoord;
  21.   begin
  22.     MaxTopLeft := CalcMaxTopLeft(FTopLeft, DrawInfo);
  23.     if ScrollBar = SB_HORZ then
  24.       Result := FTopLeft.X - MaxTopLeft.X else
  25.       Result := FTopLeft.Y - MaxTopLeft.Y;
  26.     if Result < 1 then Result := 1;
  27.   end;
  28.   function PageDown: Longint;
  29.   var
  30.     DrawInfo: TbsGridDrawInfo;
  31.   begin
  32.     CalcDrawInfo(DrawInfo);
  33.     with DrawInfo do
  34.       if ScrollBar = SB_HORZ then
  35.         Result := Horz.LastFullVisibleCell - FTopLeft.X else
  36.         Result := Vert.LastFullVisibleCell - FTopLeft.Y;
  37.     if Result < 1 then Result := 1;
  38.   end;
  39.   function CalcScrollBar(Value, ARTLFactor: Longint): Longint;
  40.   begin
  41.     Result := Value;
  42.     case ScrollCode of
  43.       SB_LINEUP:
  44.         Dec(Result, ARTLFactor);
  45.       SB_LINEDOWN:
  46.         Inc(Result, ARTLFactor);
  47.       SB_PAGEUP:
  48.         Dec(Result, PageUp * ARTLFactor);
  49.       SB_PAGEDOWN:
  50.         Inc(Result, PageDown * ARTLFactor);
  51.       SB_THUMBPOSITION, SB_THUMBTRACK:
  52.         if (goThumbTracking in Options) or (ScrollCode = SB_THUMBPOSITION) then
  53.         begin
  54.           if (not UseRightToLeftAlignment) or (ARTLFactor = 1) then
  55.             Result := Min + LongMulDiv(Pos, Max - Min, MaxShortInt)
  56.           else
  57.             Result := Max - LongMulDiv(Pos, Max - Min, MaxShortInt);
  58.         end;
  59.       SB_BOTTOM:
  60.         Result := Max;
  61.       SB_TOP:
  62.         Result := Min;
  63.     end;
  64.   end;
  65.   procedure ModifyPixelScrollBar(Code, Pos: Cardinal);
  66.   var
  67.     NewOffset: Integer;
  68.     OldOffset: Integer;
  69.     R: TGridRect;
  70.     GridSpace, ColWidth: Integer;
  71.   begin
  72.     NewOffset := FColOffset;
  73.     ColWidth := ColWidths[DrawInfo.Horz.FirstGridCell];
  74.     GridSpace := ClientWidth - DrawInfo.Horz.FixedBoundary;
  75.     case Code of
  76.       SB_LINEUP: Dec(NewOffset, Canvas.TextWidth('0') * RTLFactor);
  77.       SB_LINEDOWN: Inc(NewOffset, Canvas.TextWidth('0') * RTLFactor);
  78.       SB_PAGEUP: Dec(NewOffset, GridSpace * RTLFactor);
  79.       SB_PAGEDOWN: Inc(NewOffset, GridSpace * RTLFactor);
  80.       SB_THUMBPOSITION,
  81.       SB_THUMBTRACK:
  82.         if (goThumbTracking in Options) or (Code = SB_THUMBPOSITION) then
  83.         begin
  84.           if not UseRightToLeftAlignment then
  85.             NewOffset := Pos
  86.           else
  87.             NewOffset := Max - Integer(Pos);
  88.         end;
  89.       SB_BOTTOM: NewOffset := 0;
  90.       SB_TOP: NewOffset := ColWidth - GridSpace;
  91.     end;
  92.     if NewOffset < 0 then
  93.       NewOffset := 0
  94.     else if NewOffset >= ColWidth - GridSpace then
  95.       NewOffset := ColWidth - GridSpace;
  96.     if NewOffset <> FColOffset then
  97.     begin
  98.       OldOffset := FColOffset;
  99.       FColOffset := NewOffset;
  100.       ScrollData(OldOffset - NewOffset, 0);
  101.       FillChar(R, SizeOf(R), 0);
  102.       R.Bottom := FixedRows;
  103.       InvalidateRect(R);
  104.       Update;
  105.       UpdateScrollPos(True);
  106.     end;
  107.   end;
  108. begin
  109.   if (not UseRightToLeftAlignment) or (not UseRightToLeft) then
  110.     RTLFactor := 1
  111.   else
  112.     RTLFactor := -1;
  113.   if Visible and CanFocus and TabStop and not (csDesigning in ComponentState) then
  114.     SetFocus;
  115.   CalcDrawInfo(DrawInfo);
  116.   if (ScrollBar = SB_HORZ) and (ColCount = 1) then
  117.   begin
  118.     ModifyPixelScrollBar(ScrollCode, Pos);
  119.     Exit;
  120.   end;
  121.   MaxTopLeft.X := ColCount - 1;
  122.   MaxTopLeft.Y := RowCount - 1;
  123.   MaxTopLeft := CalcMaxTopLeft(MaxTopLeft, DrawInfo);
  124.   NewTopLeft := FTopLeft;
  125.   if ScrollBar = SB_HORZ then
  126.     repeat
  127.       NewTopLeft.X := CalcScrollBar(NewTopLeft.X, RTLFactor);
  128.     until (NewTopLeft.X <= FixedCols) or (NewTopLeft.X >= MaxTopLeft.X)
  129.       or (ColWidths[NewTopLeft.X] > 0)
  130.   else
  131.     repeat
  132.       NewTopLeft.Y := CalcScrollBar(NewTopLeft.Y, 1);
  133.     until (NewTopLeft.Y <= FixedRows) or (NewTopLeft.Y >= MaxTopLeft.Y)
  134.       or (RowHeights[NewTopLeft.Y] > 0);
  135.   NewTopLeft.X := Math.Max(FixedCols, Math.Min(MaxTopLeft.X, NewTopLeft.X));
  136.   NewTopLeft.Y := Math.Max(FixedRows, Math.Min(MaxTopLeft.Y, NewTopLeft.Y));
  137.   if (NewTopLeft.X <> FTopLeft.X) or (NewTopLeft.Y <> FTopLeft.Y) then
  138.     MoveTopLeft(NewTopLeft.X, NewTopLeft.Y);
  139. end;
  140. procedure TbsSkinCustomGrid.MoveAdjust(var CellPos: Longint; FromIndex, ToIndex: Longint);
  141. var
  142.   Min, Max: Longint;
  143. begin
  144.   if CellPos = FromIndex then CellPos := ToIndex
  145.   else
  146.   begin
  147.     Min := FromIndex;
  148.     Max := ToIndex;
  149.     if FromIndex > ToIndex then
  150.     begin
  151.       Min := ToIndex;
  152.       Max := FromIndex;
  153.     end;
  154.     if (CellPos >= Min) and (CellPos <= Max) then
  155.       if FromIndex > ToIndex then
  156.         Inc(CellPos) else
  157.         Dec(CellPos);
  158.   end;
  159. end;
  160. procedure TbsSkinCustomGrid.MoveAnchor(const NewAnchor: TGridCoord);
  161. var
  162.   OldSel: TGridRect;
  163. begin
  164.   if [goRangeSelect, goEditing] * Options = [goRangeSelect] then
  165.   begin
  166.     OldSel := Selection;
  167.     FAnchor := NewAnchor;
  168.     if goRowSelect in Options then FAnchor.X := ColCount - 1;
  169.     ClampInView(NewAnchor);
  170.     SelectionMoved(OldSel);
  171.   end
  172.   else MoveCurrent(NewAnchor.X, NewAnchor.Y, True, True);
  173. end;
  174. procedure TbsSkinCustomGrid.MoveCurrent(ACol, ARow: Longint; MoveAnchor,
  175.   Show: Boolean);
  176. var
  177.   OldSel: TGridRect;
  178.   OldCurrent: TGridCoord;
  179. begin
  180.   if (ACol < 0) or (ARow < 0) or (ACol >= ColCount) or (ARow >= RowCount) then
  181.     InvalidOp(SIndexOutOfRange);
  182.   if SelectCell(ACol, ARow) then
  183.   begin
  184.     OldSel := Selection;
  185.     OldCurrent := FCurrent;
  186.     FCurrent.X := ACol;
  187.     FCurrent.Y := ARow;
  188.     if not (goAlwaysShowEditor in Options) then HideEditor;
  189.     if MoveAnchor or not (goRangeSelect in Options) then
  190.     begin
  191.       FAnchor := FCurrent;
  192.       if goRowSelect in Options then FAnchor.X := ColCount - 1;
  193.     end;
  194.     if goRowSelect in Options then FCurrent.X := FixedCols;
  195.     if Show then ClampInView(FCurrent);
  196.     SelectionMoved(OldSel);
  197.     with OldCurrent do InvalidateCell(X, Y);
  198.     with FCurrent do InvalidateCell(ACol, ARow);
  199.   end;
  200. end;
  201. procedure TbsSkinCustomGrid.MoveTopLeft(ALeft, ATop: Longint);
  202. var
  203.   OldTopLeft: TGridCoord;
  204. begin
  205.   if (ALeft = FTopLeft.X) and (ATop = FTopLeft.Y) then Exit;
  206.   Update;
  207.   OldTopLeft := FTopLeft;
  208.   FTopLeft.X := ALeft;
  209.   FTopLeft.Y := ATop;
  210.   TopLeftMoved(OldTopLeft);
  211. end;
  212. procedure TbsSkinCustomGrid.ResizeCol(Index: Longint; OldSize, NewSize: Integer);
  213. begin
  214.   InvalidateGrid;
  215. end;
  216. procedure TbsSkinCustomGrid.ResizeRow(Index: Longint; OldSize, NewSize: Integer);
  217. begin
  218.   InvalidateGrid;
  219. end;
  220. procedure TbsSkinCustomGrid.SelectionMoved(const OldSel: TGridRect);
  221. var
  222.   OldRect, NewRect: TRect;
  223.   AXorRects: TXorRects;
  224.   I: Integer;
  225. begin
  226.   if not HandleAllocated then Exit;
  227.   GridRectToScreenRect(OldSel, OldRect, True);
  228.   GridRectToScreenRect(Selection, NewRect, True);
  229.   XorRects(OldRect, NewRect, AXorRects);
  230.   for I := Low(AXorRects) to High(AXorRects) do
  231.     Windows.InvalidateRect(Handle, @AXorRects[I], False);
  232. end;
  233. procedure TbsSkinCustomGrid.ScrollDataInfo(DX, DY: Integer;
  234.   var DrawInfo: TbsGridDrawInfo);
  235. var
  236.   ScrollArea: TRect;
  237.   ScrollFlags: Integer;
  238. begin
  239.   with DrawInfo do
  240.   begin
  241.     ScrollFlags := SW_INVALIDATE;
  242.     if not DefaultDrawing then
  243.       ScrollFlags := ScrollFlags or SW_ERASE;
  244.     { Scroll the area }
  245.     if DY = 0 then
  246.     begin
  247.       { Scroll both the column titles and data area at the same time }
  248.       if not UseRightToLeftAlignment then
  249.         ScrollArea := Rect(Horz.FixedBoundary, 0, Horz.GridExtent-1, Vert.GridExtent)
  250.       else
  251.       begin
  252.         ScrollArea := Rect(ClientWidth - Horz.GridExtent + 1, 0, ClientWidth - Horz.FixedBoundary, Vert.GridExtent);
  253.         DX := -DX;
  254.       end;
  255.       ScrollWindowEx(Handle, DX, 0, @ScrollArea, @ScrollArea, 0, nil, ScrollFlags);
  256.     end
  257.     else if DX = 0 then
  258.     begin
  259.       { Scroll both the row titles and data area at the same time }
  260.       ScrollArea := Rect(0, Vert.FixedBoundary, Horz.GridExtent, Vert.GridExtent);
  261.       ScrollWindowEx(Handle, 0, DY, @ScrollArea, @ScrollArea, 0, nil, ScrollFlags);
  262.     end
  263.     else
  264.     begin
  265.       { Scroll titles and data area separately }
  266.       { Column titles }
  267.       ScrollArea := Rect(Horz.FixedBoundary, 0, Horz.GridExtent-1, Vert.FixedBoundary);
  268.       ScrollWindowEx(Handle, DX, 0, @ScrollArea, @ScrollArea, 0, nil, ScrollFlags);
  269.       { Row titles }
  270.       ScrollArea := Rect(0, Vert.FixedBoundary, Horz.FixedBoundary, Vert.GridExtent);
  271.       ScrollWindowEx(Handle, 0, DY, @ScrollArea, @ScrollArea, 0, nil, ScrollFlags);
  272.       { Data area }
  273.       ScrollArea := Rect(Horz.FixedBoundary, Vert.FixedBoundary, Horz.GridExtent,
  274.         Vert.GridExtent);
  275.       ScrollWindowEx(Handle, DX, DY, @ScrollArea, @ScrollArea, 0, nil, ScrollFlags);
  276.     end;
  277.   end;
  278.   if goRowSelect in Options then
  279.     InvalidateRect(Selection);
  280. end;
  281. procedure TbsSkinCustomGrid.ScrollData(DX, DY: Integer);
  282. var
  283.   DrawInfo: TbsGridDrawInfo;
  284. begin
  285.   CalcDrawInfo(DrawInfo);
  286.   ScrollDataInfo(DX, DY, DrawInfo);
  287. end;
  288. procedure TbsSkinCustomGrid.TopLeftMoved(const OldTopLeft: TGridCoord);
  289.   function CalcScroll(const AxisInfo: TbsGridAxisDrawInfo;
  290.     OldPos, CurrentPos: Integer; var Amount: Longint): Boolean;
  291.   var
  292.     Start, Stop: Longint;
  293.     I: Longint;
  294.   begin
  295.     Result := False;
  296.     with AxisInfo do
  297.     begin
  298.       if OldPos < CurrentPos then
  299.       begin
  300.         Start := OldPos;
  301.         Stop := CurrentPos;
  302.       end
  303.       else
  304.       begin
  305.         Start := CurrentPos;
  306.         Stop := OldPos;
  307.       end;
  308.       Amount := 0;
  309.       for I := Start to Stop - 1 do
  310.       begin
  311.         Inc(Amount, GetExtent(I) + EffectiveLineWidth);
  312.         if Amount > (GridBoundary - FixedBoundary) then
  313.         begin
  314.           { Scroll amount too big, redraw the whole thing }
  315.           InvalidateGrid;
  316.           Exit;
  317.         end;
  318.       end;
  319.       if OldPos < CurrentPos then Amount := -Amount;
  320.     end;
  321.     Result := True;
  322.   end;
  323. var
  324.   DrawInfo: TbsGridDrawInfo;
  325.   Delta: TGridCoord;
  326.   R: TRect;
  327. begin
  328.   UpdateScrollPos(True);
  329.   CalcDrawInfo(DrawInfo);
  330.   if CalcScroll(DrawInfo.Horz, OldTopLeft.X, FTopLeft.X, Delta.X) and
  331.     CalcScroll(DrawInfo.Vert, OldTopLeft.Y, FTopLeft.Y, Delta.Y) then
  332.     ScrollDataInfo(Delta.X, Delta.Y, DrawInfo);
  333.   TopLeftChanged;
  334.   R.Left := GridWidth;
  335.   if R.Left >= Width then R.Left := Width - 1;
  336.   R.Top := 0;
  337.   R.Right := Width;
  338.   R.Bottom := Height;
  339.   Windows.InvalidateRect(Handle, @R, True);
  340. end;
  341. procedure TbsSkinCustomGrid.UpdateScrollPos;
  342. var
  343.   DrawInfo: TbsGridDrawInfo;
  344.   MaxTopLeft: TGridCoord;
  345.   GridSpace, ColWidth: Integer;
  346.   procedure SetScroll(Code: Word; Value: Integer);
  347.   begin
  348.     if UseRightToLeftAlignment and (Code = SB_HORZ) then
  349.       if ColCount <> 1 then Value := MaxShortInt - Value
  350.       else                  Value := (ColWidth - GridSpace) - Value;
  351.      case Code of
  352.        SB_HORZ:
  353.          if FHScrollBar <> nil then
  354.          begin
  355.            FHScrollBar.SimplySetPosition(Value);
  356.          end;
  357.        SB_VERT:
  358.          if (FVScrollBar <> nil) and UpDateVert then
  359.          begin
  360.            FVScrollBar.SimplySetPosition(Value);
  361.          end;
  362.      end;
  363.   end;
  364. begin
  365.   if (not HandleAllocated) then Exit;
  366.   CalcDrawInfo(DrawInfo);
  367.   MaxTopLeft.X := ColCount - 1;
  368.   MaxTopLeft.Y := RowCount - 1;
  369.   MaxTopLeft := CalcMaxTopLeft(MaxTopLeft, DrawInfo);
  370.     if ColCount = 1 then
  371.     begin
  372.       ColWidth := ColWidths[DrawInfo.Horz.FirstGridCell];
  373.       GridSpace := ClientWidth - DrawInfo.Horz.FixedBoundary;
  374.       if (FColOffset > 0) and (GridSpace > (ColWidth - FColOffset)) then
  375.         ModifyScrollbar(SB_HORZ, SB_THUMBPOSITION, ColWidth - GridSpace, True)
  376.       else
  377.         SetScroll(SB_HORZ, FColOffset)
  378.     end
  379.     else
  380.       SetScroll(SB_HORZ, LongMulDiv(FTopLeft.X - FixedCols, MaxShortInt,
  381.         MaxTopLeft.X - FixedCols));
  382.     SetScroll(SB_VERT, LongMulDiv(FTopLeft.Y - FixedRows, MaxShortInt,
  383.       MaxTopLeft.Y - FixedRows));
  384. end;
  385. type
  386.   ParentControl = class(TWinControl);
  387. procedure TbsSkinCustomGrid.UpdateScrollRange;
  388. var
  389.   MaxTopLeft, OldTopLeft: TGridCoord;
  390.   DrawInfo: TbsGridDrawInfo;
  391.   Updated: Boolean;
  392.   VVisibleChanged, HVisibleChanged: Boolean;
  393.   VVisible, HVisible: Boolean;
  394.   K: Integer;
  395.   procedure DoUpdate;
  396.   begin
  397.     if not Updated then
  398.     begin
  399.       Update;
  400.       Updated := True;
  401.     end;
  402.   end;
  403.   procedure CalcSizeInfo;
  404.   begin
  405.     CalcDrawInfoXY(DrawInfo, DrawInfo.Horz.GridExtent, DrawInfo.Vert.GridExtent);
  406.     MaxTopLeft.X := ColCount - 1;
  407.     MaxTopLeft.Y := RowCount - 1;
  408.     MaxTopLeft := CalcMaxTopLeft(MaxTopLeft, DrawInfo);
  409.   end;
  410.   procedure SetAxisRange(var Max, Old, Current: Longint; Code: Word;
  411.     Fixeds: Integer);
  412.   begin
  413.     CalcSizeInfo;
  414.     if Fixeds < Max then
  415.       begin
  416.         case Code of
  417.           SB_HORZ:
  418.             if FHScrollBar <> nil then
  419.             begin
  420.               FHScrollBar.SetRange(0, MaxShortInt, FHScrollBar.Position, 0);
  421.               K := ColCount - GetVisibleColCount - FixedCols;
  422.               if K = 0 then K := 1;
  423.               FHScrollBar.SmallChange := FHScrollBar.Max div K;
  424.               if FHScrollBar.SmallChange = 0
  425.               then FHScrollBar.SmallChange := 1;
  426.               FHScrollBar.LargeChange := FHScrollBar.SmallChange;
  427.               if not FHScrollBar.Visible
  428.               then
  429.                 begin
  430.                   HVisible := True;
  431.                   HVisibleChanged := True;
  432.                 end;
  433.             end;
  434.           SB_VERT:
  435.             if (FVScrollBar <> nil) and UpDateVert then
  436.             begin
  437.               FVScrollBar.SetRange(0, MaxShortInt, FVScrollBar.Position, 0);
  438.               FVScrollBar.SmallChange := FVScrollBar.Max div
  439.                (RowCount - GetVisibleRowCount - FixedRows);
  440.               if FVScrollBar.SmallChange = 0
  441.               then FVScrollBar.SmallChange := 1;
  442.               FVScrollBar.LargeChange := FVScrollBar.SmallChange;
  443.               if not FVScrollBar.Visible
  444.               then
  445.                 begin
  446.                   VVisibleChanged := True;
  447.                   VVisible := True;
  448.                 end;
  449.             end
  450.         end;
  451.       end
  452.     else
  453.       begin
  454.         case Code of
  455.           SB_HORZ:
  456.             if FHScrollBar <> nil then
  457.             begin
  458.               FHScrollBar.SetRange(0, 0, 0, 0);
  459.               if FHScrollBar.Visible
  460.               then
  461.                 begin
  462.                   HVisibleChanged := True;
  463.                   HVisible := False;
  464.                 end;
  465.             end;
  466.           SB_VERT:
  467.             if (FVScrollBar <> nil) and UpDateVert then
  468.             begin
  469.               FVScrollBar.SetRange(0, 0, 0, 0);
  470.               if FVScrollBar.Visible
  471.               then
  472.                 begin
  473.                   VVisibleChanged := True;
  474.                   VVisible := False;
  475.                 end;
  476.             end;
  477.         end;
  478.       end;
  479.     if Old > Max then
  480.     begin
  481.       DoUpdate;
  482.       Current := Max;
  483.     end;
  484.   end;
  485.   procedure SetHorzRange;
  486.   var
  487.     Range: Integer;
  488.   begin
  489.       if ColCount = 1 then
  490.       begin
  491.         Range := ColWidths[0] - ClientWidth;
  492.         if Range < 0 then Range := 0;
  493.         // skinscroll
  494.         if (FHScrollBar <> nil)
  495.         then
  496.           if Range > 0
  497.           then
  498.             begin
  499.               FHScrollBar.SetRange(0, Range, FHScrollBar.Position, 0);
  500.               K := ColCount - GetVisibleColCount - FixedCols;
  501.               if K = 0 then K := 1;
  502.               FHScrollBar.SmallChange := FHScrollBar.Max div K;
  503.               if FHScrollBar.SmallChange = 0
  504.               then FHScrollBar.SmallChange := 1;
  505.               FHScrollBar.LargeChange := FHScrollBar.SmallChange;
  506.               if not FHScrollBar.Visible
  507.               then
  508.                 begin
  509.                   HVisibleChanged := True;
  510.                   HVisible:= True;
  511.                 end;
  512.             end
  513.           else
  514.             if FHScrollBar.Visible
  515.             then
  516.               begin
  517.                 HVisibleChanged := True;
  518.                 HVisible:= False;
  519.               end;
  520.       end
  521.       else
  522.         SetAxisRange(MaxTopLeft.X, OldTopLeft.X, FTopLeft.X, SB_HORZ, FixedCols);
  523.   end;
  524.   procedure SetVertRange;
  525.   begin
  526.     SetAxisRange(MaxTopLeft.Y, OldTopLeft.Y, FTopLeft.Y, SB_VERT, FixedRows);
  527.   end;
  528. var
  529.   R: TRect;
  530. begin
  531.   if not HandleAllocated or not Showing or FInCheckScrollBars then Exit;
  532.   VVisibleChanged := False;
  533.   HVisibleChanged := False;
  534.   with DrawInfo do
  535.   begin
  536.     Horz.GridExtent := ClientWidth;
  537.     Vert.GridExtent := ClientHeight;
  538.   end;
  539.   OldTopLeft := FTopLeft;
  540.   { Temporarily mark us as not having scroll bars to avoid recursion }
  541.   Updated := False;
  542.   SetHorzRange;
  543.   DrawInfo.Vert.GridExtent := ClientHeight;
  544.   SetVertRange;
  545.   if DrawInfo.Horz.GridExtent <> ClientWidth then
  546.   begin
  547.     DrawInfo.Horz.GridExtent := ClientWidth;
  548.     SetHorzRange;
  549.   end;
  550.   UpdateScrollPos(True);
  551.   if (FTopLeft.X <> OldTopLeft.X) or (FTopLeft.Y <> OldTopLeft.Y) then
  552.     TopLeftMoved(OldTopLeft);
  553.   FInCheckScrollBars := True;
  554.   if VVisibleChanged then FVScrollBar.Visible := VVisible;
  555.   if HVisibleChanged then FHScrollBar.Visible := HVisible;
  556.   FInCheckScrollBars := False;
  557.   if (FVScrollBar <> nil) and (FHScrollBar <> nil)
  558.   then
  559.     begin
  560.       if not FVScrollBar.Visible and FHScrollBar.Both
  561.       then
  562.         FHScrollBar.Both := False
  563.       else
  564.         if FVScrollBar.Visible and not FHScrollBar.Both
  565.         then
  566.           FHScrollBar.Both := True;
  567.     end;
  568.   if (Self.Align <> alNone) and (HVisibleChanged or VVisibleChanged)
  569.   then
  570.     begin
  571.       R := Parent.ClientRect;
  572.       FInCheckScrollBars := True;
  573.       ParentControl(Parent).AlignControls(nil, R);
  574.       Invalidate;
  575.       FInCheckScrollBars := False;
  576.     end;
  577. end;
  578. function TbsSkinCustomGrid.CreateEditor: TbsSkinInplaceEdit;
  579. begin
  580.   Result := TbsSkinInplaceEdit.Create(Self);
  581. end;
  582. procedure TbsSkinCustomGrid.CreateParams(var Params: TCreateParams);
  583. begin
  584.   inherited CreateParams(Params);
  585.   with Params do
  586.   begin
  587.     Style := Style or WS_TABSTOP;
  588.     Style := Style and not WS_VSCROLL;
  589.     Style := Style and not WS_HSCROLL;
  590.     WindowClass.style := CS_DBLCLKS;
  591.   end;
  592. end;
  593. procedure TbsSkinCustomGrid.KeyDown(var Key: Word; Shift: TShiftState);
  594. var
  595.   NewTopLeft, NewCurrent, MaxTopLeft: TGridCoord;
  596.   DrawInfo: TbsGridDrawInfo;
  597.   PageWidth, PageHeight: Integer;
  598.   RTLFactor: Integer;
  599.   procedure CalcPageExtents;
  600.   begin
  601.     CalcDrawInfo(DrawInfo);
  602.     PageWidth := DrawInfo.Horz.LastFullVisibleCell - LeftCol;
  603.     if PageWidth < 1 then PageWidth := 1;
  604.     PageHeight := DrawInfo.Vert.LastFullVisibleCell - TopRow;
  605.     if PageHeight < 1 then PageHeight := 1;
  606.   end;
  607.   procedure Restrict(var Coord: TGridCoord; MinX, MinY, MaxX, MaxY: Longint);
  608.   begin
  609.     with Coord do
  610.     begin
  611.       if X > MaxX then X := MaxX
  612.       else if X < MinX then X := MinX;
  613.       if Y > MaxY then Y := MaxY
  614.       else if Y < MinY then Y := MinY;
  615.     end;
  616.   end;
  617. begin
  618.   inherited KeyDown(Key, Shift);
  619.   if not CanGridAcceptKey(Key, Shift) then Key := 0;
  620.   if not UseRightToLeftAlignment then
  621.     RTLFactor := 1
  622.   else
  623.     RTLFactor := -1;
  624.   NewCurrent := FCurrent;
  625.   NewTopLeft := FTopLeft;
  626.   CalcPageExtents;
  627.   if ssCtrl in Shift then
  628.     case Key of
  629.       VK_UP: Dec(NewTopLeft.Y);
  630.       VK_DOWN: Inc(NewTopLeft.Y);
  631.       VK_LEFT:
  632.         if not (goRowSelect in Options) then
  633.         begin
  634.           Dec(NewCurrent.X, PageWidth * RTLFactor);
  635.           Dec(NewTopLeft.X, PageWidth * RTLFactor);
  636.         end;
  637.       VK_RIGHT:
  638.         if not (goRowSelect in Options) then
  639.         begin
  640.           Inc(NewCurrent.X, PageWidth * RTLFactor);
  641.           Inc(NewTopLeft.X, PageWidth * RTLFactor);
  642.         end;
  643.       VK_PRIOR: NewCurrent.Y := TopRow;
  644.       VK_NEXT: NewCurrent.Y := DrawInfo.Vert.LastFullVisibleCell;
  645.       VK_HOME:
  646.         begin
  647.           NewCurrent.X := FixedCols;
  648.           NewCurrent.Y := FixedRows;
  649.         end;
  650.       VK_END:
  651.         begin
  652.           NewCurrent.X := ColCount - 1;
  653.           NewCurrent.Y := RowCount - 1;
  654.         end;
  655.     end
  656.   else
  657.     case Key of
  658.       VK_UP: Dec(NewCurrent.Y);
  659.       VK_DOWN: Inc(NewCurrent.Y);
  660.       VK_LEFT:
  661.         if goRowSelect in Options then
  662.           Dec(NewCurrent.Y, RTLFactor) else
  663.           Dec(NewCurrent.X, RTLFactor);
  664.       VK_RIGHT:
  665.         if goRowSelect in Options then
  666.           Inc(NewCurrent.Y, RTLFactor) else
  667.           Inc(NewCurrent.X, RTLFactor);
  668.       VK_NEXT:
  669.         begin
  670.           Inc(NewCurrent.Y, PageHeight);
  671.           Inc(NewTopLeft.Y, PageHeight);
  672.         end;
  673.       VK_PRIOR:
  674.         begin
  675.           Dec(NewCurrent.Y, PageHeight);
  676.           Dec(NewTopLeft.Y, PageHeight);
  677.         end;
  678.       VK_HOME:
  679.         if goRowSelect in Options then
  680.           NewCurrent.Y := FixedRows else
  681.           NewCurrent.X := FixedCols;
  682.       VK_END:
  683.         if goRowSelect in Options then
  684.           NewCurrent.Y := RowCount - 1 else
  685.           NewCurrent.X := ColCount - 1;
  686.       VK_TAB:
  687.         if not (ssAlt in Shift) then
  688.         repeat
  689.           if ssShift in Shift then
  690.           begin
  691.             Dec(NewCurrent.X);
  692.             if NewCurrent.X < FixedCols then
  693.             begin
  694.               NewCurrent.X := ColCount - 1;
  695.               Dec(NewCurrent.Y);
  696.               if NewCurrent.Y < FixedRows then NewCurrent.Y := RowCount - 1;
  697.             end;
  698.             Shift := [];
  699.           end
  700.           else
  701.           begin
  702.             Inc(NewCurrent.X);
  703.             if NewCurrent.X >= ColCount then
  704.             begin
  705.               NewCurrent.X := FixedCols;
  706.               Inc(NewCurrent.Y);
  707.               if NewCurrent.Y >= RowCount then NewCurrent.Y := FixedRows;
  708.             end;
  709.           end;
  710.         until TabStops[NewCurrent.X] or (NewCurrent.X = FCurrent.X);
  711.       VK_F2: EditorMode := True;
  712.     end;
  713.   MaxTopLeft.X := ColCount - 1;
  714.   MaxTopLeft.Y := RowCount - 1;
  715.   MaxTopLeft := CalcMaxTopLeft(MaxTopLeft, DrawInfo);
  716.   Restrict(NewTopLeft, FixedCols, FixedRows, MaxTopLeft.X, MaxTopLeft.Y);
  717.   if (NewTopLeft.X <> LeftCol) or (NewTopLeft.Y <> TopRow) then
  718.     MoveTopLeft(NewTopLeft.X, NewTopLeft.Y);
  719.   Restrict(NewCurrent, FixedCols, FixedRows, ColCount - 1, RowCount - 1);
  720.   if (NewCurrent.X <> Col) or (NewCurrent.Y <> Row) then
  721.     FocusCell(NewCurrent.X, NewCurrent.Y, not (ssShift in Shift));
  722. end;
  723. procedure TbsSkinCustomGrid.KeyPress(var Key: Char);
  724. begin
  725.   inherited KeyPress(Key);
  726.   if not (goAlwaysShowEditor in Options) and (Key = #13) then
  727.   begin
  728.     if FEditorMode then
  729.       HideEditor else
  730.       ShowEditor;
  731.     Key := #0;
  732.   end;
  733. end;
  734. procedure TbsSkinCustomGrid.MouseDown(Button: TMouseButton; Shift: TShiftState;
  735.   X, Y: Integer);
  736. var
  737.   CellHit: TGridCoord;
  738.   DrawInfo: TbsGridDrawInfo;
  739.   MoveDrawn: Boolean;
  740. begin
  741.   MoveDrawn := False;
  742.   HideEdit;
  743.   if not (csDesigning in ComponentState) and
  744.     (CanFocus or (GetParentForm(Self) = nil)) then
  745.   begin
  746.     SetFocus;
  747.     if not IsActiveControl then
  748.     begin
  749.       MouseCapture := False;
  750.       Exit;
  751.     end;
  752.   end;
  753.   if (Button = mbLeft) and (ssDouble in Shift) then
  754.     DblClick
  755.   else if Button = mbLeft then
  756.   begin
  757.     CalcDrawInfo(DrawInfo);
  758.     { Check grid sizing }
  759.     CalcSizingState(X, Y, FGridState, FSizingIndex, FSizingPos, FSizingOfs,
  760.       DrawInfo);
  761.     if FGridState <> gsNormal then
  762.     begin
  763.       if UseRightToLeftAlignment then
  764.         FSizingPos := ClientWidth - FSizingPos;
  765.       DrawSizingLine(DrawInfo);
  766.       Exit;
  767.     end;
  768.     CellHit := CalcCoordFromPoint(X, Y, DrawInfo);
  769.     if (CellHit.X >= FixedCols) and (CellHit.Y >= FixedRows) then
  770.     begin
  771.       if goEditing in Options then
  772.       begin
  773.         if (CellHit.X = FCurrent.X) and (CellHit.Y = FCurrent.Y) then
  774.           ShowEditor
  775.         else
  776.         begin
  777.           MoveCurrent(CellHit.X, CellHit.Y, True, True);
  778.           UpdateEdit;
  779.         end;
  780.         Click;
  781.       end
  782.       else
  783.       begin
  784.         FGridState := gsSelecting;
  785.         SetTimer(Handle, 1, 60, nil);
  786.         if ssShift in Shift then
  787.           MoveAnchor(CellHit)
  788.         else
  789.           MoveCurrent(CellHit.X, CellHit.Y, True, True);
  790.       end;
  791.     end
  792.     else if (goRowMoving in Options) and (CellHit.X >= 0) and
  793.       (CellHit.X < FixedCols) and (CellHit.Y >= FixedRows) then
  794.     begin
  795.       FMoveIndex := CellHit.Y;
  796.       FMovePos := FMoveIndex;
  797.       if BeginRowDrag(FMoveIndex, FMovePos, Point(X,Y)) then
  798.       begin
  799.         FGridState := gsRowMoving;
  800.         Update;
  801.         DrawMove;
  802.         MoveDrawn := True;
  803.         SetTimer(Handle, 1, 60, nil);
  804.       end;
  805.     end
  806.     else if (goColMoving in Options) and (CellHit.Y >= 0) and
  807.       (CellHit.Y < FixedRows) and (CellHit.X >= FixedCols) then
  808.     begin
  809.       FMoveIndex := CellHit.X;
  810.       FMovePos := FMoveIndex;
  811.       if BeginColumnDrag(FMoveIndex, FMovePos, Point(X,Y)) then
  812.       begin
  813.         FGridState := gsColMoving;
  814.         Update;
  815.         DrawMove;
  816.         MoveDrawn := True;
  817.         SetTimer(Handle, 1, 60, nil);
  818.       end;
  819.     end;
  820.   end;
  821.   try
  822.     inherited MouseDown(Button, Shift, X, Y);
  823.   except
  824.     if MoveDrawn then DrawMove;
  825.   end;
  826. end;
  827. procedure TbsSkinCustomGrid.MouseMove(Shift: TShiftState; X, Y: Integer);
  828. var
  829.   DrawInfo: TbsGridDrawInfo;
  830.   CellHit: TGridCoord;
  831. begin
  832.   CalcDrawInfo(DrawInfo);
  833.   case FGridState of
  834.     gsSelecting, gsColMoving, gsRowMoving:
  835.       begin
  836.         CellHit := CalcCoordFromPoint(X, Y, DrawInfo);
  837.         if (CellHit.X >= FixedCols) and (CellHit.Y >= FixedRows) and
  838.           (CellHit.X <= DrawInfo.Horz.LastFullVisibleCell+1) and
  839.           (CellHit.Y <= DrawInfo.Vert.LastFullVisibleCell+1) then
  840.           case FGridState of
  841.             gsSelecting:
  842.               if ((CellHit.X <> FAnchor.X) or (CellHit.Y <> FAnchor.Y)) then
  843.                 MoveAnchor(CellHit);
  844.             gsColMoving:
  845.               MoveAndScroll(X, CellHit.X, DrawInfo, DrawInfo.Horz, SB_HORZ, Point(X,Y));
  846.             gsRowMoving:
  847.               MoveAndScroll(Y, CellHit.Y, DrawInfo, DrawInfo.Vert, SB_VERT, Point(X,Y));
  848.           end;
  849.       end;
  850.     gsRowSizing, gsColSizing:
  851.       begin
  852.         DrawSizingLine(DrawInfo); { XOR it out }
  853.         if FGridState = gsRowSizing then
  854.           FSizingPos := Y + FSizingOfs else
  855.           FSizingPos := X + FSizingOfs;
  856.         DrawSizingLine(DrawInfo); { XOR it back in }
  857.       end;
  858.   end;
  859.   inherited MouseMove(Shift, X, Y);
  860. end;
  861. procedure TbsSkinCustomGrid.MouseUp(Button: TMouseButton; Shift: TShiftState;
  862.   X, Y: Integer);
  863. var
  864.   DrawInfo: TbsGridDrawInfo;
  865.   NewSize: Integer;
  866.   function ResizeLine(const AxisInfo: TbsGridAxisDrawInfo): Integer;
  867.   var
  868.     I: Integer;
  869.   begin
  870.     with AxisInfo do
  871.     begin
  872.       Result := FixedBoundary;
  873.       for I := FirstGridCell to FSizingIndex - 1 do
  874.         Inc(Result, GetExtent(I) + EffectiveLineWidth);
  875.       Result := FSizingPos - Result;
  876.     end;
  877.   end;
  878. begin
  879.   try
  880.     case FGridState of
  881.       gsSelecting:
  882.         begin
  883.           MouseMove(Shift, X, Y);
  884.           KillTimer(Handle, 1);
  885.           UpdateEdit;
  886.           Click;
  887.         end;
  888.       gsRowSizing, gsColSizing:
  889.         begin
  890.           CalcDrawInfo(DrawInfo);
  891.           DrawSizingLine(DrawInfo);
  892.           if UseRightToLeftAlignment then
  893.             FSizingPos := ClientWidth - FSizingPos;
  894.           if FGridState = gsColSizing then
  895.           begin
  896.             NewSize := ResizeLine(DrawInfo.Horz);
  897.             if NewSize > 1 then
  898.             begin
  899.               ColWidths[FSizingIndex] := NewSize;
  900.               UpdateDesigner;
  901.             end;
  902.           end
  903.           else
  904.           begin
  905.             NewSize := ResizeLine(DrawInfo.Vert);
  906.             if NewSize > 1 then
  907.             begin
  908.               RowHeights[FSizingIndex] := NewSize;
  909.               UpdateDesigner;
  910.             end;
  911.           end;
  912.         end;
  913.       gsColMoving:
  914.         begin
  915.           DrawMove;
  916.           KillTimer(Handle, 1);
  917.           if EndColumnDrag(FMoveIndex, FMovePos, Point(X,Y))
  918.             and (FMoveIndex <> FMovePos) then
  919.           begin
  920.             MoveColumn(FMoveIndex, FMovePos);
  921.             UpdateDesigner;
  922.           end;
  923.           UpdateEdit;
  924.         end;
  925.       gsRowMoving:
  926.         begin
  927.           DrawMove;
  928.           KillTimer(Handle, 1);
  929.           if EndRowDrag(FMoveIndex, FMovePos, Point(X,Y))
  930.             and (FMoveIndex <> FMovePos) then
  931.           begin
  932.             MoveRow(FMoveIndex, FMovePos);
  933.             UpdateDesigner;
  934.           end;
  935.           UpdateEdit;
  936.         end;
  937.     else
  938.       UpdateEdit;
  939.     end;
  940.     inherited MouseUp(Button, Shift, X, Y);
  941.   finally
  942.     FGridState := gsNormal;
  943.   end;
  944. end;
  945. procedure TbsSkinCustomGrid.MoveAndScroll(Mouse, CellHit: Integer;
  946.   var DrawInfo: TbsGridDrawInfo; var Axis: TbsGridAxisDrawInfo;
  947.   ScrollBar: Integer; const MousePt: TPoint);
  948. begin
  949.   if UseRightToLeftAlignment and (ScrollBar = SB_HORZ) then
  950.     Mouse := ClientWidth - Mouse;
  951.   if (CellHit <> FMovePos) and
  952.     not((FMovePos = Axis.FixedCellCount) and (Mouse < Axis.FixedBoundary)) and
  953.     not((FMovePos = Axis.GridCellCount-1) and (Mouse > Axis.GridBoundary)) then
  954.   begin
  955.     DrawMove;   // hide the drag line
  956.     if (Mouse < Axis.FixedBoundary) then
  957.     begin
  958.       if (FMovePos > Axis.FixedCellCount) then
  959.       begin
  960.         ModifyScrollbar(ScrollBar, SB_LINEUP, 0, False);
  961.         Update;
  962.         CalcDrawInfo(DrawInfo);    // this changes contents of Axis var
  963.       end;
  964.       CellHit := Axis.FirstGridCell;
  965.     end
  966.     else if (Mouse >= Axis.FullVisBoundary) then
  967.     begin
  968.       if (FMovePos = Axis.LastFullVisibleCell) and
  969.         (FMovePos < Axis.GridCellCount -1) then
  970.       begin
  971.         ModifyScrollBar(Scrollbar, SB_LINEDOWN, 0, False);
  972.         Update;
  973.         CalcDrawInfo(DrawInfo);    // this changes contents of Axis var
  974.       end;
  975.       CellHit := Axis.LastFullVisibleCell;
  976.     end
  977.     else if CellHit < 0 then CellHit := FMovePos;
  978.     if ((FGridState = gsColMoving) and CheckColumnDrag(FMoveIndex, CellHit, MousePt))
  979.       or ((FGridState = gsRowMoving) and CheckRowDrag(FMoveIndex, CellHit, MousePt)) then
  980.       FMovePos := CellHit;
  981.     DrawMove;
  982.   end;
  983. end;
  984. function TbsSkinCustomGrid.GetColWidths(Index: Longint): Integer;
  985. begin
  986.   if (FColWidths = nil) or (Index >= ColCount) then
  987.     Result := DefaultColWidth
  988.   else
  989.     Result := PIntArray(FColWidths)^[Index + 1];
  990. end;
  991. function TbsSkinCustomGrid.GetRowHeights(Index: Longint): Integer;
  992. begin
  993.   if (FRowHeights = nil) or (Index >= RowCount) then
  994.     Result := DefaultRowHeight
  995.   else
  996.     Result := PIntArray(FRowHeights)^[Index + 1];
  997. end;
  998. function TbsSkinCustomGrid.GetGridWidth: Integer;
  999. var
  1000.   DrawInfo: TbsGridDrawInfo;
  1001. begin
  1002.   CalcDrawInfo(DrawInfo);
  1003.   Result := DrawInfo.Horz.GridBoundary;
  1004. end;
  1005. function TbsSkinCustomGrid.GetGridHeight: Integer;
  1006. var
  1007.   DrawInfo: TbsGridDrawInfo;
  1008. begin
  1009.   CalcDrawInfo(DrawInfo);
  1010.   Result := DrawInfo.Vert.GridBoundary;
  1011. end;
  1012. function TbsSkinCustomGrid.GetSelection: TGridRect;
  1013. begin
  1014.   Result := GridRect(FCurrent, FAnchor);
  1015. end;
  1016. function TbsSkinCustomGrid.GetTabStops(Index: Longint): Boolean;
  1017. begin
  1018.   if FTabStops = nil then Result := True
  1019.   else Result := Boolean(PIntArray(FTabStops)^[Index + 1]);
  1020. end;
  1021. function TbsSkinCustomGrid.GetVisibleColCount: Integer;
  1022. var
  1023.   DrawInfo: TbsGridDrawInfo;
  1024. begin
  1025.   CalcDrawInfo(DrawInfo);
  1026.   Result := DrawInfo.Horz.LastFullVisibleCell - LeftCol + 1;
  1027. end;
  1028. function TbsSkinCustomGrid.GetVisibleRowCount: Integer;
  1029. var
  1030.   DrawInfo: TbsGridDrawInfo;
  1031. begin
  1032.   CalcDrawInfo(DrawInfo);
  1033.   Result := DrawInfo.Vert.LastFullVisibleCell - TopRow + 1;
  1034. end;
  1035. procedure TbsSkinCustomGrid.SetBorderStyle(Value: TBorderStyle);
  1036. begin
  1037.   if FBorderStyle <> Value then
  1038.   begin
  1039.     FBorderStyle := Value;
  1040.     RecreateWnd;
  1041.   end;
  1042. end;
  1043. procedure TbsSkinCustomGrid.SetCol(Value: Longint);
  1044. begin
  1045.   if Col <> Value then FocusCell(Value, Row, True);
  1046. end;
  1047. procedure TbsSkinCustomGrid.SetColCount(Value: Longint);
  1048. begin
  1049.   if FColCount <> Value then
  1050.   begin
  1051.     if Value < 1 then Value := 1;
  1052.     if Value <= FixedCols then FixedCols := Value - 1;
  1053.     ChangeSize(Value, RowCount);
  1054.     if goRowSelect in Options then
  1055.     begin
  1056.       FAnchor.X := ColCount - 1;
  1057.       Invalidate;
  1058.     end;
  1059.   end;
  1060. end;
  1061. procedure TbsSkinCustomGrid.SetColWidths(Index: Longint; Value: Integer);
  1062. begin
  1063.   if FColWidths = nil then
  1064.     UpdateExtents(FColWidths, ColCount, DefaultColWidth);
  1065.   if Index >= ColCount then InvalidOp(SIndexOutOfRange);
  1066.   if Value <> PIntArray(FColWidths)^[Index + 1] then
  1067.   begin
  1068.     ResizeCol(Index, PIntArray(FColWidths)^[Index + 1], Value);
  1069.     PIntArray(FColWidths)^[Index + 1] := Value;
  1070.     ColWidthsChanged;
  1071.   end;
  1072. end;
  1073. procedure TbsSkinCustomGrid.SetDefaultColWidth(Value: Integer);
  1074. begin
  1075.   if FColWidths <> nil then UpdateExtents(FColWidths, 0, 0);
  1076.   FDefaultColWidth := Value;
  1077.   ColWidthsChanged;
  1078.   InvalidateGrid;
  1079. end;
  1080. procedure TbsSkinCustomGrid.SetDefaultRowHeight(Value: Integer);
  1081. begin
  1082.   if FRowHeights <> nil then UpdateExtents(FRowHeights, 0, 0);
  1083.   FDefaultRowHeight := Value;
  1084.   RowHeightsChanged;
  1085.   InvalidateGrid;
  1086. end;
  1087. procedure TbsSkinCustomGrid.SetFixedColor(Value: TColor);
  1088. begin
  1089.   if FFixedColor <> Value then
  1090.   begin
  1091.     FFixedColor := Value;
  1092.     InvalidateGrid;
  1093.   end;
  1094. end;
  1095. procedure TbsSkinCustomGrid.SetFixedCols(Value: Integer);
  1096. begin
  1097.   if FFixedCols <> Value then
  1098.   begin
  1099.     if Value < 0 then InvalidOp(SIndexOutOfRange);
  1100.     if Value >= ColCount then InvalidOp(SFixedColTooBig);
  1101.     FFixedCols := Value;
  1102.     Initialize;
  1103.     InvalidateGrid;
  1104.   end;
  1105. end;
  1106. procedure TbsSkinCustomGrid.SetFixedRows(Value: Integer);
  1107. begin
  1108.   if FFixedRows <> Value then
  1109.   begin
  1110.     if Value < 0 then InvalidOp(SIndexOutOfRange);
  1111.     if Value >= RowCount then InvalidOp(SFixedRowTooBig);
  1112.     FFixedRows := Value;
  1113.     Initialize;
  1114.     InvalidateGrid;
  1115.   end;
  1116. end;
  1117. procedure TbsSkinCustomGrid.SetEditorMode(Value: Boolean);
  1118. begin
  1119.   if not Value then
  1120.     HideEditor
  1121.   else
  1122.   begin
  1123.     ShowEditor;
  1124.     if FInplaceEdit <> nil then FInplaceEdit.Deselect;
  1125.   end;
  1126. end;
  1127. procedure TbsSkinCustomGrid.SetGridLineWidth(Value: Integer);
  1128. begin
  1129.   if FGridLineWidth <> Value then
  1130.   begin
  1131.     FGridLineWidth := Value;
  1132.     InvalidateGrid;
  1133.   end;
  1134. end;
  1135. procedure TbsSkinCustomGrid.SetLeftCol(Value: Longint);
  1136. begin
  1137.   if FTopLeft.X <> Value then MoveTopLeft(Value, TopRow);
  1138. end;
  1139. procedure TbsSkinCustomGrid.SetOptions(Value: TGridOptions);
  1140. begin
  1141.   if FOptions <> Value then
  1142.   begin
  1143.     if goRowSelect in Value then
  1144.       Exclude(Value, goAlwaysShowEditor);
  1145.     FOptions := Value;
  1146.     if not FEditorMode then
  1147.       if goAlwaysShowEditor in Value then
  1148.         ShowEditor else
  1149.         HideEditor;
  1150.     if goRowSelect in Value then MoveCurrent(Col, Row,  True, False);
  1151.     InvalidateGrid;
  1152.   end;
  1153. end;
  1154. procedure TbsSkinCustomGrid.SetRow(Value: Longint);
  1155. begin
  1156.   if Row <> Value then FocusCell(Col, Value, True);
  1157. end;
  1158. procedure TbsSkinCustomGrid.SetRowCount(Value: Longint);
  1159. begin
  1160.   if FRowCount <> Value then
  1161.   begin
  1162.     if Value < 1 then Value := 1;
  1163.     if Value <= FixedRows then FixedRows := Value - 1;
  1164.     ChangeSize(ColCount, Value);
  1165.   end;
  1166. end;
  1167. procedure TbsSkinCustomGrid.SetRowHeights(Index: Longint; Value: Integer);
  1168. begin
  1169.   if FRowHeights = nil then
  1170.     UpdateExtents(FRowHeights, RowCount, DefaultRowHeight);
  1171.   if Index >= RowCount then InvalidOp(SIndexOutOfRange);
  1172.   if Value <> PIntArray(FRowHeights)^[Index + 1] then
  1173.   begin
  1174.     ResizeRow(Index, PIntArray(FRowHeights)^[Index + 1], Value);
  1175.     PIntArray(FRowHeights)^[Index + 1] := Value;
  1176.     RowHeightsChanged;
  1177.   end;
  1178. end;
  1179. procedure TbsSkinCustomGrid.SetSelection(Value: TGridRect);
  1180. var
  1181.   OldSel: TGridRect;
  1182. begin
  1183.   OldSel := Selection;
  1184.   FAnchor := Value.TopLeft;
  1185.   FCurrent := Value.BottomRight;
  1186.   SelectionMoved(OldSel);
  1187. end;
  1188. procedure TbsSkinCustomGrid.SetTabStops(Index: Longint; Value: Boolean);
  1189. begin
  1190.   if FTabStops = nil then
  1191.     UpdateExtents(FTabStops, ColCount, Integer(True));
  1192.   if Index >= ColCount then InvalidOp(SIndexOutOfRange);
  1193.   PIntArray(FTabStops)^[Index + 1] := Integer(Value);
  1194. end;
  1195. procedure TbsSkinCustomGrid.SetTopRow(Value: Longint);
  1196. begin
  1197.   if FTopLeft.Y <> Value then MoveTopLeft(LeftCol, Value);
  1198. end;
  1199. procedure TbsSkinCustomGrid.HideEdit;
  1200. begin
  1201.   if FInplaceEdit <> nil then
  1202.     try
  1203.       UpdateText;
  1204.     finally
  1205.       FInplaceCol := -1;
  1206.       FInplaceRow := -1;
  1207.       FInplaceEdit.Hide;
  1208.     end;
  1209. end;
  1210. procedure TbsSkinCustomGrid.UpdateEdit;
  1211.   procedure UpdateEditor;
  1212.   begin
  1213.     FInplaceCol := Col;
  1214.     FInplaceRow := Row;
  1215.     if FIndex > -1
  1216.     then
  1217.       begin
  1218.         FInplaceEdit.Color := BGColor;
  1219.         FInplaceEdit.Font.Name := FontName;
  1220.         FInplaceEdit.Font.Color := FontColor;
  1221.         FInplaceEdit.Font.Style := FontStyle;
  1222.         FInplaceEdit.Font.Height := FontHeight;
  1223.         FInplaceEdit.Font.CharSet := Self.Font.CharSet;
  1224.       end
  1225.     else
  1226.       begin
  1227.         FInplaceEdit.Color := clWindow;
  1228.         FInplaceEdit.Font := Font;
  1229.       end;
  1230.     FInplaceEdit.UpdateContents;
  1231.     if FInplaceEdit.MaxLength = -1 then FCanEditModify := False
  1232.     else FCanEditModify := True;
  1233.     FInplaceEdit.SelectAll;
  1234.   end;
  1235. begin
  1236.   if CanEditShow then
  1237.   begin
  1238.     if FInplaceEdit = nil then
  1239.     begin
  1240.       FInplaceEdit := CreateEditor;
  1241.       FInplaceEdit.SetGrid(Self);
  1242.       FInplaceEdit.Parent := Self;
  1243.       UpdateEditor;
  1244.     end
  1245.     else
  1246.     begin
  1247.       if (Col <> FInplaceCol) or (Row <> FInplaceRow) then
  1248.       begin
  1249.         HideEdit;
  1250.         UpdateEditor;
  1251.       end;
  1252.     end;
  1253.     if CanEditShow then FInplaceEdit.Move(CellRect(Col, Row));
  1254.   end;
  1255. end;
  1256. procedure TbsSkinCustomGrid.UpdateText;
  1257. begin
  1258.   if (FInplaceCol <> -1) and (FInplaceRow <> -1) then
  1259.     SetEditText(FInplaceCol, FInplaceRow, FInplaceEdit.Text);
  1260. end;
  1261. procedure TbsSkinCustomGrid.WMChar(var Msg: TWMChar);
  1262. begin
  1263.   if (goEditing in Options) and (Char(Msg.CharCode) in [^H, #32..#255]) then
  1264.     ShowEditorChar(Char(Msg.CharCode))
  1265.   else
  1266.     inherited;
  1267. end;
  1268. procedure TbsSkinCustomGrid.WMCommand(var Message: TWMCommand);
  1269. begin
  1270.   with Message do
  1271.   begin
  1272.     if (FInplaceEdit <> nil) and (Ctl = FInplaceEdit.Handle) then
  1273.       case NotifyCode of
  1274.         EN_CHANGE: UpdateText;
  1275.       end;
  1276.   end;
  1277. end;
  1278. procedure TbsSkinCustomGrid.WMGetDlgCode(var Msg: TWMGetDlgCode);
  1279. begin
  1280.   Msg.Result := DLGC_WANTARROWS;
  1281.   if goRowSelect in Options then Exit;
  1282.   if goTabs in Options then Msg.Result := Msg.Result or DLGC_WANTTAB;
  1283.   if goEditing in Options then Msg.Result := Msg.Result or DLGC_WANTCHARS;
  1284. end;
  1285. procedure TbsSkinCustomGrid.WMKillFocus(var Msg: TWMKillFocus);
  1286. begin
  1287.   inherited;
  1288.   InvalidateRect(Selection);
  1289.   if (FInplaceEdit <> nil) and (Msg.FocusedWnd <> FInplaceEdit.Handle) then
  1290.     HideEdit;
  1291. end;
  1292. procedure TbsSkinCustomGrid.WMLButtonDown(var Message: TMessage);
  1293. begin
  1294.   inherited;
  1295.   if FInplaceEdit <> nil then FInplaceEdit.FClickTime := GetMessageTime;
  1296. end;
  1297. procedure TbsSkinCustomGrid.WMNCHitTest(var Msg: TWMNCHitTest);
  1298. begin
  1299.   DefaultHandler(Msg);
  1300.   FHitTest := ScreenToClient(SmallPointToPoint(Msg.Pos));
  1301. end;
  1302. procedure TbsSkinCustomGrid.WMSetCursor(var Msg: TWMSetCursor);
  1303. var
  1304.   DrawInfo: TbsGridDrawInfo;
  1305.   State: TbsGridState;
  1306.   Index: Longint;
  1307.   Pos, Ofs: Integer;
  1308.   Cur: HCURSOR;
  1309. begin
  1310.   Cur := 0;
  1311.   with Msg do
  1312.   begin
  1313.     if HitTest = HTCLIENT then
  1314.     begin
  1315.       if FGridState = gsNormal then
  1316.       begin
  1317.         CalcDrawInfo(DrawInfo);
  1318.         CalcSizingState(FHitTest.X, FHitTest.Y, State, Index, Pos, Ofs,
  1319.           DrawInfo);
  1320.       end else State := FGridState;
  1321.       if State = gsRowSizing then
  1322.         Cur := Screen.Cursors[crVSplit]
  1323.       else if State = gsColSizing then
  1324.         Cur := Screen.Cursors[crHSplit]
  1325.     end;
  1326.   end;
  1327.   if Cur <> 0 then SetCursor(Cur)
  1328.   else inherited;
  1329. end;
  1330. procedure TbsSkinCustomGrid.WMSetFocus(var Msg: TWMSetFocus);
  1331. begin
  1332.   inherited;
  1333.   if (FInplaceEdit = nil) or (Msg.FocusedWnd <> FInplaceEdit.Handle) then
  1334.   begin
  1335.     InvalidateRect(Selection);
  1336.     UpdateEdit;
  1337.   end;
  1338. end;
  1339. procedure TbsSkinCustomGrid.WMSize(var Msg: TWMSize);
  1340. begin
  1341.   inherited;
  1342.   if (UseRightToLeftAlignment) or ((FIndex = -1) and (goRowSelect in Options))
  1343.   then Invalidate;
  1344.   if not FInCheckScrollBars then UpdateScrollRange(True);
  1345. end;
  1346. procedure TbsSkinCustomGrid.WMVScroll(var Msg: TWMVScroll);
  1347. begin
  1348.   ModifyScrollBar(SB_VERT, Msg.ScrollCode, Msg.Pos, True);
  1349. end;
  1350. procedure TbsSkinCustomGrid.WMHScroll(var Msg: TWMHScroll);
  1351. begin
  1352.   ModifyScrollBar(SB_HORZ, Msg.ScrollCode, Msg.Pos, True);
  1353. end;
  1354. procedure TbsSkinCustomGrid.CancelMode;
  1355. var
  1356.   DrawInfo: TbsGridDrawInfo;
  1357. begin
  1358.   try
  1359.     case FGridState of
  1360.       gsSelecting:
  1361.         KillTimer(Handle, 1);
  1362.       gsRowSizing, gsColSizing:
  1363.         begin
  1364.           CalcDrawInfo(DrawInfo);
  1365.           DrawSizingLine(DrawInfo);
  1366.         end;
  1367.       gsColMoving, gsRowMoving:
  1368.         begin
  1369.           DrawMove;
  1370.           KillTimer(Handle, 1);
  1371.         end;
  1372.     end;
  1373.   finally
  1374.     FGridState := gsNormal;
  1375.   end;
  1376. end;
  1377. procedure TbsSkinCustomGrid.WMCancelMode(var Msg: TWMCancelMode);
  1378. begin
  1379.   inherited;
  1380.   CancelMode;
  1381. end;
  1382. procedure TbsSkinCustomGrid.CMCancelMode(var Msg: TMessage);
  1383. begin
  1384.   if Assigned(FInplaceEdit) then FInplaceEdit.WndProc(Msg);
  1385.   inherited;
  1386.   CancelMode;
  1387. end;
  1388. procedure TbsSkinCustomGrid.CMFontChanged(var Message: TMessage);
  1389. begin
  1390.   if FInplaceEdit <> nil then FInplaceEdit.Font := Font;
  1391.   inherited;
  1392. end;
  1393. procedure TbsSkinCustomGrid.CMDesignHitTest(var Msg: TCMDesignHitTest);
  1394. begin
  1395.   Msg.Result := Longint(BOOL(Sizing(Msg.Pos.X, Msg.Pos.Y)));
  1396. end;
  1397. procedure TbsSkinCustomGrid.CMWanTbsecialKey(var Msg: TCMWantSpecialKey);
  1398. begin
  1399.   inherited;
  1400.   if (goEditing in Options) and (Char(Msg.CharCode) = #13) then Msg.Result := 1;
  1401. end;
  1402. procedure TbsSkinCustomGrid.TimedScroll(Direction: TGridScrollDirection);
  1403. var
  1404.   MaxAnchor, NewAnchor: TGridCoord;
  1405. begin
  1406.   NewAnchor := FAnchor;
  1407.   MaxAnchor.X := ColCount - 1;
  1408.   MaxAnchor.Y := RowCount - 1;
  1409.   if (sdLeft in Direction) and (FAnchor.X > FixedCols) then Dec(NewAnchor.X);
  1410.   if (sdRight in Direction) and (FAnchor.X < MaxAnchor.X) then Inc(NewAnchor.X);
  1411.   if (sdUp in Direction) and (FAnchor.Y > FixedRows) then Dec(NewAnchor.Y);
  1412.   if (sdDown in Direction) and (FAnchor.Y < MaxAnchor.Y) then Inc(NewAnchor.Y);
  1413.   if (FAnchor.X <> NewAnchor.X) or (FAnchor.Y <> NewAnchor.Y) then
  1414.     MoveAnchor(NewAnchor);
  1415. end;
  1416. procedure TbsSkinCustomGrid.WMTimer(var Msg: TWMTimer);
  1417. var
  1418.   Point: TPoint;
  1419.   DrawInfo: TbsGridDrawInfo;
  1420.   ScrollDirection: TGridScrollDirection;
  1421.   CellHit: TGridCoord;
  1422.   LeftSide: Integer;
  1423.   RightSide: Integer;
  1424. begin
  1425.   if not (FGridState in [gsSelecting, gsRowMoving, gsColMoving]) then Exit;
  1426.   GetCursorPos(Point);
  1427.   Point := ScreenToClient(Point);
  1428.   CalcDrawInfo(DrawInfo);
  1429.   ScrollDirection := [];
  1430.   with DrawInfo do
  1431.   begin
  1432.     CellHit := CalcCoordFromPoint(Point.X, Point.Y, DrawInfo);
  1433.     case FGridState of
  1434.       gsColMoving:
  1435.         MoveAndScroll(Point.X, CellHit.X, DrawInfo, Horz, SB_HORZ, Point);
  1436.       gsRowMoving:
  1437.         MoveAndScroll(Point.Y, CellHit.Y, DrawInfo, Vert, SB_VERT, Point);
  1438.       gsSelecting:
  1439.       begin
  1440.         if not UseRightToLeftAlignment then
  1441.         begin
  1442.           if Point.X < Horz.FixedBoundary then Include(ScrollDirection, sdLeft)
  1443.           else if Point.X > Horz.FullVisBoundary then Include(ScrollDirection, sdRight);
  1444.         end
  1445.         else
  1446.         begin
  1447.           LeftSide := ClientWidth - Horz.FullVisBoundary;
  1448.           RightSide := ClientWidth - Horz.FixedBoundary;
  1449.           if Point.X < LeftSide then Include(ScrollDirection, sdRight)
  1450.           else if Point.X > RightSide then Include(ScrollDirection, sdLeft);
  1451.         end;
  1452.         if Point.Y < Vert.FixedBoundary then Include(ScrollDirection, sdUp)
  1453.         else if Point.Y > Vert.FullVisBoundary then Include(ScrollDirection, sdDown);
  1454.         if ScrollDirection <> [] then  TimedScroll(ScrollDirection);
  1455.       end;
  1456.     end;
  1457.   end;
  1458. end;
  1459. procedure TbsSkinCustomGrid.ColWidthsChanged;
  1460. begin
  1461.   UpdateScrollRange(True);
  1462.   UpdateEdit;
  1463. end;
  1464. procedure TbsSkinCustomGrid.RowHeightsChanged;
  1465. begin
  1466.   UpdateScrollRange(True);
  1467.   UpdateEdit;
  1468. end;
  1469. procedure TbsSkinCustomGrid.DeleteColumn(ACol: Longint);
  1470. begin
  1471.   MoveColumn(ACol, ColCount-1);
  1472.   ColCount := ColCount - 1;
  1473. end;
  1474. procedure TbsSkinCustomGrid.DeleteRow(ARow: Longint);
  1475. begin
  1476.   MoveRow(ARow, RowCount - 1);
  1477.   RowCount := RowCount - 1;
  1478. end;
  1479. procedure TbsSkinCustomGrid.UpdateDesigner;
  1480. var
  1481.   ParentForm: TCustomForm;
  1482. begin
  1483.   if (csDesigning in ComponentState) and HandleAllocated and
  1484.     not (csUpdating in ComponentState) then
  1485.   begin
  1486.     ParentForm := GetParentForm(Self);
  1487.     if Assigned(ParentForm) and Assigned(ParentForm.Designer) then
  1488.       ParentForm.Designer.Modified;
  1489.   end;
  1490. end;
  1491. function TbsSkinCustomGrid.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean;
  1492. begin
  1493.   Result := inherited DoMouseWheelDown(Shift, MousePos);
  1494.   if not Result then
  1495.   begin
  1496.     if Row < RowCount - 1 then Row := Row + 1;
  1497.     Result := True;
  1498.   end;
  1499. end;
  1500. function TbsSkinCustomGrid.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean;
  1501. begin
  1502.   Result := inherited DoMouseWheelUp(Shift, MousePos);
  1503.   if not Result then
  1504.   begin
  1505.     if Row > FixedRows then Row := Row - 1;
  1506.     Result := True;
  1507.   end;
  1508. end;
  1509. function TbsSkinCustomGrid.CheckColumnDrag(var Origin,
  1510.   Destination: Integer; const MousePt: TPoint): Boolean;
  1511. begin
  1512.   Result := True;
  1513. end;
  1514. function TbsSkinCustomGrid.CheckRowDrag(var Origin,
  1515.   Destination: Integer; const MousePt: TPoint): Boolean;
  1516. begin
  1517.   Result := True;
  1518. end;
  1519. function TbsSkinCustomGrid.BeginColumnDrag(var Origin, Destination: Integer; const MousePt: TPoint): Boolean;
  1520. begin
  1521.   Result := True;
  1522. end;
  1523. function TbsSkinCustomGrid.BeginRowDrag(var Origin, Destination: Integer; const MousePt: TPoint): Boolean;
  1524. begin
  1525.   Result := True;
  1526. end;
  1527. function TbsSkinCustomGrid.EndColumnDrag(var Origin, Destination: Integer; const MousePt: TPoint): Boolean;
  1528. begin
  1529.   Result := True;
  1530. end;
  1531. function TbsSkinCustomGrid.EndRowDrag(var Origin, Destination: Integer; const MousePt: TPoint): Boolean;
  1532. begin
  1533.   Result := True;
  1534. end;
  1535. procedure TbsSkinCustomGrid.WMPAINT;
  1536. begin
  1537.   inherited;
  1538.   if not FInCheckScrollBars then UpDateScrollRange(True);
  1539. end;
  1540. procedure TbsSkinCustomGrid.CMShowingChanged(var Message: TMessage);
  1541. begin
  1542.   inherited;
  1543. end;
  1544. { TbsSkinDrawGrid }
  1545. function TbsSkinDrawGrid.CellRect(ACol, ARow: Longint): TRect;
  1546. begin
  1547.   Result := inherited CellRect(ACol, ARow);
  1548. end;
  1549. procedure TbsSkinDrawGrid.MouseToCell(X, Y: Integer; var ACol, ARow: Longint);
  1550. var
  1551.   Coord: TGridCoord;
  1552. begin
  1553.   Coord := MouseCoord(X, Y);
  1554.   ACol := Coord.X;
  1555.   ARow := Coord.Y;
  1556. end;
  1557. procedure TbsSkinDrawGrid.ColumnMoved(FromIndex, ToIndex: Longint);
  1558. begin
  1559.   if Assigned(FOnColumnMoved) then FOnColumnMoved(Self, FromIndex, ToIndex);
  1560. end;
  1561. function TbsSkinDrawGrid.GetEditMask(ACol, ARow: Longint): string;
  1562. begin
  1563.   Result := '';
  1564.   if Assigned(FOnGetEditMask) then FOnGetEditMask(Self, ACol, ARow, Result);
  1565. end;
  1566. function TbsSkinDrawGrid.GetEditText(ACol, ARow: Longint): string;
  1567. begin
  1568.   Result := '';
  1569.   if Assigned(FOnGetEditText) then FOnGetEditText(Self, ACol, ARow, Result);
  1570. end;
  1571. procedure TbsSkinDrawGrid.RowMoved(FromIndex, ToIndex: Longint);
  1572. begin
  1573.   if Assigned(FOnRowMoved) then FOnRowMoved(Self, FromIndex, ToIndex);
  1574. end;
  1575. function TbsSkinDrawGrid.SelectCell(ACol, ARow: Longint): Boolean;
  1576. begin
  1577.   Result := True;
  1578.   if Assigned(FOnSelectCell) then FOnSelectCell(Self, ACol, ARow, Result);
  1579. end;
  1580. procedure TbsSkinDrawGrid.SetEditText(ACol, ARow: Longint; const Value: string);
  1581. begin
  1582.   if Assigned(FOnSetEditText) then FOnSetEditText(Self, ACol, ARow, Value);
  1583. end;
  1584. procedure TbsSkinDrawGrid.DrawCell(ACol, ARow: Longint; ARect: TRect;
  1585.   AState: TGridDrawState);
  1586. var
  1587.   Hold: Integer;
  1588. begin
  1589.   if Assigned(FOnDrawCell) then
  1590.   begin
  1591.     if UseRightToLeftAlignment then
  1592.     begin
  1593.       ARect.Left := ClientWidth - ARect.Left;
  1594.       ARect.Right := ClientWidth - ARect.Right;
  1595.       Hold := ARect.Left;
  1596.       ARect.Left := ARect.Right;
  1597.       ARect.Right := Hold;
  1598.       ChangeGridOrientation(False);
  1599.     end;
  1600.     FOnDrawCell(Self, ACol, ARow, ARect, AState);
  1601.     if UseRightToLeftAlignment then ChangeGridOrientation(True);
  1602.   end;
  1603. end;
  1604. procedure TbsSkinDrawGrid.TopLeftChanged;
  1605. begin
  1606.   inherited TopLeftChanged;
  1607.   if Assigned(FOnTopLeftChanged) then FOnTopLeftChanged(Self);
  1608. end;
  1609. { StrItem management for TStringSparseList }
  1610. type
  1611.   PStrItem = ^TStrItem;
  1612.   TStrItem = record
  1613.     FObject: TObject;
  1614.     FString: string;
  1615.   end;
  1616. function NewStrItem(const AString: string; AObject: TObject): PStrItem;
  1617. begin
  1618.   New(Result);
  1619.   Result^.FObject := AObject;
  1620.   Result^.FString := AString;
  1621. end;
  1622. procedure DisposeStrItem(P: PStrItem);
  1623. begin
  1624.   Dispose(P);
  1625. end;
  1626. { Sparse array classes for TbsSkinStringGrid }
  1627. type
  1628.   PPointer = ^Pointer;
  1629. { Exception classes }
  1630.   EStringSparseListError = class(Exception);
  1631. { TbsarsePointerArray class}
  1632. { Used by TbsarseList.  Based on Sparse1Array, but has Pointer elements
  1633.   and Integer index, just like TPointerList/TList, and less indirection }
  1634.   { Apply function for the applicator:
  1635.         TheIndex        Index of item in array
  1636.         TheItem         Value of item (i.e pointer element) in section
  1637.         Returns: 0 if success, else error code. }
  1638.   TbsAApply = function(TheIndex: Integer; TheItem: Pointer): Integer;
  1639.   TSecDir = array[0..4095] of Pointer;  { Enough for up to 12 bits of sec }
  1640.   PSecDir = ^TSecDir;
  1641.   TbsAQuantum = (SPASmall, SPALarge);   { Section size }
  1642.   TbsarsePointerArray = class(TObject)
  1643.   private
  1644.     secDir: PSecDir;
  1645.     slotsInDir: Word;
  1646.     indexMask, secShift: Word;
  1647.     FHighBound: Integer;
  1648.     FSectionSize: Word;
  1649.     cachedIndex: Integer;
  1650.     cachedPointer: Pointer;
  1651.     { Return item[i], nil if slot outside defined section. }
  1652.     function  GetAt(Index: Integer): Pointer;
  1653.     { Return address of item[i], creating slot if necessary. }
  1654.     function  MakeAt(Index: Integer): PPointer;
  1655.     { Store item at item[i], creating slot if necessary. }
  1656.     procedure PutAt(Index: Integer; Item: Pointer);
  1657.   public
  1658.     constructor Create(Quantum: TbsAQuantum);
  1659.     destructor  Destroy; override;
  1660.     { Traverse SPA, calling apply function for each defined non-nil
  1661.       item.  The traversal terminates if the apply function returns
  1662.       a value other than 0. }
  1663.     { NOTE: must be static method so that we can take its address in
  1664.       TbsarseList.ForAll }
  1665.     function  ForAll(ApplyFunction: Pointer {TbsAApply}): Integer;
  1666.     { Ratchet down HighBound after a deletion }
  1667.     procedure ResetHighBound;
  1668.     property HighBound: Integer read FHighBound;
  1669.     property SectionSize: Word read FSectionSize;
  1670.     property Items[Index: Integer]: Pointer read GetAt write PutAt; default;
  1671.   end;
  1672. { TbsarseList class }
  1673.   TbsarseList = class(TObject)
  1674.   private
  1675.     FList: TbsarsePointerArray;
  1676.     FCount: Integer;    { 1 + HighBound, adjusted for Insert/Delete }
  1677.     FQuantum: TbsAQuantum;
  1678.     procedure NewList(Quantum: TbsAQuantum);
  1679.   protected
  1680.     procedure Error; virtual;
  1681.     function  Get(Index: Integer): Pointer;
  1682.     procedure Put(Index: Integer; Item: Pointer);
  1683.   public
  1684.     constructor Create(Quantum: TbsAQuantum);
  1685.     destructor  Destroy; override;
  1686.     procedure Clear;
  1687.     procedure Delete(Index: Integer);
  1688.     procedure Exchange(Index1, Index2: Integer);
  1689.     function ForAll(ApplyFunction: Pointer {TbsAApply}): Integer;
  1690.     procedure Insert(Index: Integer; Item: Pointer);
  1691.     procedure Move(CurIndex, NewIndex: Integer);
  1692.     property Count: Integer read FCount;
  1693.     property Items[Index: Integer]: Pointer read Get write Put; default;
  1694.   end;
  1695. { TStringSparseList class }
  1696.   TStringSparseList = class(TStrings)
  1697.   private
  1698.     FList: TbsarseList;                 { of StrItems }
  1699.     FOnChange: TNotifyEvent;
  1700.   protected
  1701.     function  Get(Index: Integer): String; override;
  1702.     function  GetCount: Integer; override;
  1703.     function  GetObject(Index: Integer): TObject; override;
  1704.     procedure Put(Index: Integer; const S: String); override;
  1705.     procedure PutObject(Index: Integer; AObject: TObject); override;
  1706.     procedure Changed; virtual;
  1707.     procedure Error; virtual;
  1708.   public
  1709.     constructor Create(Quantum: TbsAQuantum);
  1710.     destructor  Destroy; override;
  1711.     procedure ReadData(Reader: TReader);
  1712.     procedure WriteData(Writer: TWriter);
  1713.     procedure DefineProperties(Filer: TFiler); override;
  1714.     procedure Delete(Index: Integer); override;
  1715.     procedure Exchange(Index1, Index2: Integer); override;
  1716.     procedure Insert(Index: Integer; const S: String); override;
  1717.     procedure Clear; override;
  1718.     property List: TbsarseList read FList;
  1719.     property OnChange: TNotifyEvent read FOnChange write FOnChange;
  1720.   end;
  1721. { TbsarsePointerArray }
  1722. const
  1723.   SPAIndexMask: array[TbsAQuantum] of Byte = (15, 255);
  1724.   SPASecShift: array[TbsAQuantum] of Byte = (4, 8);
  1725. { Expand Section Directory to cover at least `newSlots' slots. Returns: Possibly
  1726.   updated pointer to the Section Directory. }
  1727. function  ExpandDir(secDir: PSecDir; var slotsInDir: Word;
  1728.   newSlots: Word): PSecDir;
  1729. begin
  1730.   Result := secDir;
  1731.   ReallocMem(Result, newSlots * SizeOf(Pointer));
  1732.   FillChar(Result^[slotsInDir], (newSlots - slotsInDir) * SizeOf(Pointer), 0);
  1733.   slotsInDir := newSlots;
  1734. end;
  1735. { Allocate a section and set all its items to nil. Returns: Pointer to start of
  1736.   section. }
  1737. function  MakeSec(SecIndex: Integer; SectionSize: Word): Pointer;
  1738. var
  1739.   SecP: Pointer;
  1740.   Size: Word;
  1741. begin
  1742.   Size := SectionSize * SizeOf(Pointer);
  1743.   GetMem(secP, size);
  1744.   FillChar(secP^, size, 0);
  1745.   MakeSec := SecP
  1746. end;
  1747. constructor TbsarsePointerArray.Create(Quantum: TbsAQuantum);
  1748. begin
  1749.   SecDir := nil;
  1750.   SlotsInDir := 0;
  1751.   FHighBound := -1;
  1752.   FSectionSize := Word(SPAIndexMask[Quantum]) + 1;
  1753.   IndexMask := Word(SPAIndexMask[Quantum]);
  1754.   SecShift := Word(SPASecShift[Quantum]);
  1755.   CachedIndex := -1
  1756. end;
  1757. destructor TbsarsePointerArray.Destroy;
  1758. var
  1759.   i:  Integer;
  1760.   size: Word;
  1761. begin
  1762.   { Scan section directory and free each section that exists. }
  1763.   i := 0;
  1764.   size := FSectionSize * SizeOf(Pointer);
  1765.   while i < slotsInDir do begin
  1766.     if secDir^[i] <> nil then
  1767.       FreeMem(secDir^[i], size);
  1768.     Inc(i)
  1769.   end;
  1770.   { Free section directory. }
  1771.   if secDir <> nil then
  1772.     FreeMem(secDir, slotsInDir * SizeOf(Pointer));
  1773. end;
  1774. function  TbsarsePointerArray.GetAt(Index: Integer): Pointer;
  1775. var
  1776.   byteP: PChar;
  1777.   secIndex: Cardinal;
  1778. begin
  1779.   { Index into Section Directory using high order part of
  1780.     index.  Get pointer to Section. If not null, index into
  1781.     Section using low order part of index. }
  1782.   if Index = cachedIndex then
  1783.     Result := cachedPointer
  1784.   else begin
  1785.     secIndex := Index shr secShift;
  1786.     if secIndex >= slotsInDir then
  1787.       byteP := nil
  1788.     else begin
  1789.       byteP := secDir^[secIndex];
  1790.       if byteP <> nil then begin
  1791.         Inc(byteP, (Index and indexMask) * SizeOf(Pointer));
  1792.       end
  1793.     end;
  1794.     if byteP = nil then Result := nil else Result := PPointer(byteP)^;
  1795.     cachedIndex := Index;
  1796.     cachedPointer := Result
  1797.   end
  1798. end;
  1799. function  TbsarsePointerArray.MakeAt(Index: Integer): PPointer;
  1800. var
  1801.   dirP: PSecDir;
  1802.   p: Pointer;
  1803.   byteP: PChar;
  1804.   secIndex: Word;
  1805. begin
  1806.   { Expand Section Directory if necessary. }
  1807.   secIndex := Index shr secShift;       { Unsigned shift }
  1808.   if secIndex >= slotsInDir then
  1809.     dirP := expandDir(secDir, slotsInDir, secIndex + 1)
  1810.   else
  1811.     dirP := secDir;
  1812.   { Index into Section Directory using high order part of
  1813.     index.  Get pointer to Section. If null, create new
  1814.     Section.  Index into Section using low order part of index. }
  1815.   secDir := dirP;
  1816.   p := dirP^[secIndex];
  1817.   if p = nil then begin
  1818.     p := makeSec(secIndex, FSectionSize);
  1819.     dirP^[secIndex] := p
  1820.   end;
  1821.   byteP := p;
  1822.   Inc(byteP, (Index and indexMask) * SizeOf(Pointer));
  1823.   if Index > FHighBound then
  1824.     FHighBound := Index;
  1825.   Result := PPointer(byteP);
  1826.   cachedIndex := -1
  1827. end;
  1828. procedure TbsarsePointerArray.PutAt(Index: Integer; Item: Pointer);
  1829. begin
  1830.   if (Item <> nil) or (GetAt(Index) <> nil) then
  1831.   begin
  1832.     MakeAt(Index)^ := Item;
  1833.     if Item = nil then
  1834.       ResetHighBound
  1835.   end
  1836. end;
  1837. function  TbsarsePointerArray.ForAll(ApplyFunction: Pointer {TbsAApply}):
  1838.   Integer;
  1839. var
  1840.   itemP: PChar;                         { Pointer to item in section }
  1841.   item: Pointer;
  1842.   i, callerBP: Cardinal;
  1843.   j, index: Integer;
  1844. begin
  1845.   { Scan section directory and scan each section that exists,
  1846.     calling the apply function for each non-nil item.
  1847.     The apply function must be a far local function in the scope of
  1848.     the procedure P calling ForAll.  The trick of setting up the stack
  1849.     frame (taken from TurboVision's TCollection.ForEach) allows the
  1850.     apply function access to P's arguments and local variables and,
  1851.     if P is a method, the instance variables and methods of P's class }
  1852.   Result := 0;
  1853.   i := 0;
  1854.   asm
  1855.     mov   eax,[ebp]                     { Set up stack frame for local }
  1856.     mov   callerBP,eax
  1857.   end;
  1858.   while (i < slotsInDir) and (Result = 0) do begin
  1859.     itemP := secDir^[i];
  1860.     if itemP <> nil then begin
  1861.       j := 0;
  1862.       index := i shl SecShift;
  1863.       while (j < FSectionSize) and (Result = 0) do begin
  1864.         item := PPointer(itemP)^;
  1865.         if item <> nil then
  1866.           { ret := ApplyFunction(index, item.Ptr); }
  1867.           asm
  1868.             mov   eax,index
  1869.             mov   edx,item
  1870.             push  callerBP
  1871.             call  ApplyFunction
  1872.             pop   ecx
  1873.             mov   @Result,eax
  1874.           end;
  1875.         Inc(itemP, SizeOf(Pointer));
  1876.         Inc(j);
  1877.         Inc(index)
  1878.       end
  1879.     end;
  1880.     Inc(i)
  1881.   end;
  1882. end;
  1883. procedure TbsarsePointerArray.ResetHighBound;
  1884. var
  1885.   NewHighBound: Integer;
  1886.   function  Detector(TheIndex: Integer; TheItem: Pointer): Integer; far;
  1887.   begin
  1888.     if TheIndex > FHighBound then
  1889.       Result := 1
  1890.     else
  1891.     begin
  1892.       Result := 0;
  1893.       if TheItem <> nil then NewHighBound := TheIndex
  1894.     end
  1895.   end;
  1896. begin
  1897.   NewHighBound := -1;
  1898.   ForAll(@Detector);
  1899.   FHighBound := NewHighBound
  1900. end;
  1901. { TbsarseList }
  1902. constructor TbsarseList.Create(Quantum: TbsAQuantum);
  1903. begin
  1904.   NewList(Quantum)
  1905. end;
  1906. destructor TbsarseList.Destroy;
  1907. begin
  1908.   if FList <> nil then FList.Destroy
  1909. end;
  1910. procedure TbsarseList.Clear;
  1911. begin
  1912.   FList.Destroy;
  1913.   NewList(FQuantum);
  1914.   FCount := 0
  1915. end;
  1916. procedure TbsarseList.Delete(Index: Integer);
  1917. var
  1918.   I: Integer;
  1919. begin
  1920.   if (Index < 0) or (Index >= FCount) then Exit;
  1921.   for I := Index to FCount - 1 do
  1922.     FList[I] := FList[I + 1];
  1923.   FList[FCount] := nil;
  1924.   Dec(FCount);
  1925. end;
  1926. procedure TbsarseList.Error;
  1927. begin
  1928.   raise EListError.Create('List index out of bounds (%d)');
  1929. end;
  1930. procedure TbsarseList.Exchange(Index1, Index2: Integer);
  1931. var
  1932.   temp: Pointer;
  1933. begin
  1934.   temp := Get(Index1);
  1935.   Put(Index1, Get(Index2));
  1936.   Put(Index2, temp);
  1937. end;
  1938. { Jump to TbsarsePointerArray.ForAll so that it looks like it was called
  1939.   from our caller, so that the BP trick works. }
  1940. function TbsarseList.ForAll(ApplyFunction: Pointer {TbsAApply}): Integer; assembler;
  1941. asm
  1942.         MOV     EAX,[EAX].TbsarseList.FList
  1943.         JMP     TbsarsePointerArray.ForAll
  1944. end;
  1945. function  TbsarseList.Get(Index: Integer): Pointer;
  1946. begin
  1947.   if Index < 0 then Error;
  1948.   Result := FList[Index]
  1949. end;
  1950. procedure TbsarseList.Insert(Index: Integer; Item: Pointer);
  1951. var
  1952.   i: Integer;
  1953. begin
  1954.   if Index < 0 then Error;
  1955.   I := FCount;
  1956.   while I > Index do
  1957.   begin
  1958.     FList[i] := FList[i - 1];
  1959.     Dec(i)
  1960.   end;
  1961.   FList[Index] := Item;
  1962.   if Index > FCount then FCount := Index;
  1963.   Inc(FCount)
  1964. end;
  1965. procedure TbsarseList.Move(CurIndex, NewIndex: Integer);
  1966. var
  1967.   Item: Pointer;
  1968. begin
  1969.   if CurIndex <> NewIndex then
  1970.   begin
  1971.     Item := Get(CurIndex);
  1972.     Delete(CurIndex);
  1973.     Insert(NewIndex, Item);
  1974.   end;
  1975. end;
  1976. procedure TbsarseList.NewList(Quantum: TbsAQuantum);
  1977. begin
  1978.   FQuantum := Quantum;
  1979.   FList := TbsarsePointerArray.Create(Quantum)
  1980. end;
  1981. procedure TbsarseList.Put(Index: Integer; Item: Pointer);
  1982. begin
  1983.   if Index < 0 then Error;
  1984.   FList[Index] := Item;
  1985.   FCount := FList.HighBound + 1
  1986. end;
  1987. { TStringSparseList }
  1988. constructor TStringSparseList.Create(Quantum: TbsAQuantum);
  1989. begin
  1990.   FList := TbsarseList.Create(Quantum)
  1991. end;
  1992. destructor  TStringSparseList.Destroy;
  1993. begin
  1994.   if FList <> nil then begin
  1995.     Clear;
  1996.     FList.Destroy
  1997.   end
  1998. end;
  1999. procedure TStringSparseList.ReadData(Reader: TReader);
  2000. var
  2001.   i: Integer;
  2002. begin
  2003.   with Reader do begin
  2004.     i := Integer(ReadInteger);
  2005.     while i > 0 do begin
  2006.       InsertObject(Integer(ReadInteger), ReadString, nil);
  2007.       Dec(i)
  2008.     end
  2009.   end
  2010. end;
  2011. procedure TStringSparseList.WriteData(Writer: TWriter);
  2012. var
  2013.   itemCount: Integer;
  2014.   function  CountItem(TheIndex: Integer; TheItem: Pointer): Integer; far;
  2015.   begin
  2016.     Inc(itemCount);
  2017.     Result := 0
  2018.   end;
  2019.   function  StoreItem(TheIndex: Integer; TheItem: Pointer): Integer; far;
  2020.   begin
  2021.     with Writer do
  2022.     begin
  2023.       WriteInteger(TheIndex);           { Item index }
  2024.       WriteString(PStrItem(TheItem)^.FString);
  2025.     end;
  2026.     Result := 0
  2027.   end;
  2028. begin
  2029.   with Writer do
  2030.   begin
  2031.     itemCount := 0;
  2032.     FList.ForAll(@CountItem);
  2033.     WriteInteger(itemCount);
  2034.     FList.ForAll(@StoreItem);
  2035.   end
  2036. end;
  2037. procedure TStringSparseList.DefineProperties(Filer: TFiler);
  2038. begin
  2039.   Filer.DefineProperty('List', ReadData, WriteData, True);
  2040. end;
  2041. function  TStringSparseList.Get(Index: Integer): String;
  2042. var
  2043.   p: PStrItem;
  2044. begin
  2045.   p := PStrItem(FList[Index]);
  2046.   if p = nil then Result := '' else Result := p^.FString
  2047. end;
  2048. function  TStringSparseList.GetCount: Integer;
  2049. begin
  2050.   Result := FList.Count
  2051. end;
  2052. function  TStringSparseList.GetObject(Index: Integer): TObject;
  2053. var
  2054.   p: PStrItem;
  2055. begin
  2056.   p := PStrItem(FList[Index]);
  2057.   if p = nil then Result := nil else Result := p^.FObject
  2058. end;
  2059. procedure TStringSparseList.Put(Index: Integer; const S: String);
  2060. var
  2061.   p: PStrItem;
  2062.   obj: TObject;
  2063. begin
  2064.   p := PStrItem(FList[Index]);
  2065.   if p = nil then obj := nil else obj := p^.FObject;
  2066.   if (S = '') and (obj = nil) then   { Nothing left to store }
  2067.     FList[Index] := nil
  2068.   else
  2069.     FList[Index] := NewStrItem(S, obj);
  2070.   if p <> nil then DisposeStrItem(p);
  2071.   Changed
  2072. end;
  2073. procedure TStringSparseList.PutObject(Index: Integer; AObject: TObject);
  2074. var
  2075.   p: PStrItem;
  2076. begin
  2077.   p := PStrItem(FList[Index]);
  2078.   if p <> nil then
  2079.     p^.FObject := AObject
  2080.   else if AObject <> nil then
  2081.     FList[Index] := NewStrItem('',AObject);
  2082.   Changed
  2083. end;
  2084. procedure TStringSparseList.Changed;
  2085. begin
  2086.   if Assigned(FOnChange) then FOnChange(Self)
  2087. end;
  2088. procedure TStringSparseList.Error;
  2089. begin
  2090.   raise EStringSparseListError.Create(SPutObjectError);
  2091. end;
  2092. procedure TStringSparseList.Delete(Index: Integer);
  2093. var
  2094.   p: PStrItem;
  2095. begin
  2096.   p := PStrItem(FList[Index]);
  2097.   if p <> nil then DisposeStrItem(p);
  2098.   FList.Delete(Index);
  2099.   Changed
  2100. end;
  2101. procedure TStringSparseList.Exchange(Index1, Index2: Integer);
  2102. begin
  2103.   FList.Exchange(Index1, Index2);
  2104. end;
  2105. procedure TStringSparseList.Insert(Index: Integer; const S: String);
  2106. begin
  2107.   FList.Insert(Index, NewStrItem(S, nil));
  2108.   Changed
  2109. end;
  2110. procedure TStringSparseList.Clear;
  2111.   function  ClearItem(TheIndex: Integer; TheItem: Pointer): Integer; far;
  2112.   begin
  2113.     DisposeStrItem(PStrItem(TheItem));    { Item guaranteed non-nil }
  2114.     Result := 0
  2115.   end;
  2116. begin
  2117.   FList.ForAll(@ClearItem);
  2118.   FList.Clear;
  2119.   Changed
  2120. end;
  2121. { TbsSkinStringGridStrings }
  2122. { AIndex < 0 is a column (for column -AIndex - 1)
  2123.   AIndex > 0 is a row (for row AIndex - 1)
  2124.   AIndex = 0 denotes an empty row or column }
  2125. constructor TbsSkinStringGridStrings.Create(AGrid: TbsSkinStringGrid; AIndex: Longint);
  2126. begin
  2127.   inherited Create;
  2128.   FGrid := AGrid;
  2129.   FIndex := AIndex;
  2130. end;
  2131. procedure TbsSkinStringGridStrings.Assign(Source: TPersistent);
  2132. var
  2133.   I, Max: Integer;
  2134. begin
  2135.   if Source is TStrings then
  2136.   begin
  2137.     BeginUpdate;
  2138.     Max := TStrings(Source).Count - 1;
  2139.     if Max >= Count then Max := Count - 1;
  2140.     try
  2141.       for I := 0 to Max do
  2142.       begin
  2143.         Put(I, TStrings(Source).Strings[I]);
  2144.         PutObject(I, TStrings(Source).Objects[I]);
  2145.       end;
  2146.     finally
  2147.       EndUpdate;
  2148.     end;
  2149.     Exit;
  2150.   end;
  2151.   inherited Assign(Source);
  2152. end;
  2153. procedure TbsSkinStringGridStrings.CalcXY(Index: Integer; var X, Y: Integer);
  2154. begin
  2155.   if FIndex = 0 then
  2156.   begin
  2157.     X := -1; Y := -1;
  2158.   end else if FIndex > 0 then
  2159.   begin
  2160.     X := Index;
  2161.     Y := FIndex - 1;
  2162.   end else
  2163.   begin
  2164.     X := -FIndex - 1;
  2165.     Y := Index;
  2166.   end;
  2167. end;
  2168. { Changes the meaning of Add to mean copy to the first empty string }
  2169. function TbsSkinStringGridStrings.Add(const S: string): Integer;
  2170. var
  2171.   I: Integer;
  2172. begin
  2173.   for I := 0 to Count - 1 do
  2174.     if Strings[I] = '' then
  2175.     begin
  2176.       Strings[I] := S;
  2177.       Result := I;
  2178.       Exit;
  2179.     end;
  2180.   Result := -1;
  2181. end;
  2182. procedure TbsSkinStringGridStrings.Clear;
  2183. var
  2184.   SSList: TStringSparseList;
  2185.   I: Integer;
  2186.   function BlankStr(TheIndex: Integer; TheItem: Pointer): Integer; far;
  2187.   begin
  2188.     Objects[TheIndex] := nil;
  2189.     Strings[TheIndex] := '';
  2190.     Result := 0;
  2191.   end;
  2192. begin
  2193.   if FIndex > 0 then
  2194.   begin
  2195.     SSList := TStringSparseList(TbsarseList(FGrid.FData)[FIndex - 1]);
  2196.     if SSList <> nil then SSList.List.ForAll(@BlankStr);
  2197.   end
  2198.   else if FIndex < 0 then
  2199.     for I := Count - 1 downto 0 do
  2200.     begin
  2201.       Objects[I] := nil;
  2202.       Strings[I] := '';
  2203.     end;
  2204. end;
  2205. procedure TbsSkinStringGridStrings.Delete(Index: Integer);
  2206. begin
  2207.   InvalidOp(sInvalidStringGridOp);
  2208. end;
  2209. function TbsSkinStringGridStrings.Get(Index: Integer): string;
  2210. var
  2211.   X, Y: Integer;
  2212. begin
  2213.   CalcXY(Index, X, Y);
  2214.   if X < 0 then Result := '' else Result := FGrid.Cells[X, Y];
  2215. end;
  2216. function TbsSkinStringGridStrings.GetCount: Integer;
  2217. begin
  2218.   { Count of a row is the column count, and vice versa }
  2219.   if FIndex = 0 then Result := 0
  2220.   else if FIndex > 0 then Result := Integer(FGrid.ColCount)
  2221.   else Result := Integer(FGrid.RowCount);
  2222. end;
  2223. function TbsSkinStringGridStrings.GetObject(Index: Integer): TObject;
  2224. var
  2225.   X, Y: Integer;
  2226. begin
  2227.   CalcXY(Index, X, Y);
  2228.   if X < 0 then Result := nil else Result := FGrid.Objects[X, Y];
  2229. end;
  2230. procedure TbsSkinStringGridStrings.Insert(Index: Integer; const S: string);
  2231. begin
  2232.   InvalidOp(sInvalidStringGridOp);
  2233. end;
  2234. procedure TbsSkinStringGridStrings.Put(Index: Integer; const S: string);
  2235. var
  2236.   X, Y: Integer;
  2237. begin
  2238.   CalcXY(Index, X, Y);
  2239.   FGrid.Cells[X, Y] := S;
  2240. end;
  2241. procedure TbsSkinStringGridStrings.PutObject(Index: Integer; AObject: TObject);
  2242. var
  2243.   X, Y: Integer;
  2244. begin
  2245.   CalcXY(Index, X, Y);
  2246.   FGrid.Objects[X, Y] := AObject;
  2247. end;
  2248. procedure TbsSkinStringGridStrings.SetUpdateState(Updating: Boolean);
  2249. begin
  2250.   FGrid.SetUpdateState(Updating);
  2251. end;
  2252. { TbsSkinStringGrid }
  2253. constructor TbsSkinStringGrid.Create(AOwner: TComponent);
  2254. begin
  2255.   inherited Create(AOwner);
  2256.   Initialize;
  2257. end;
  2258. destructor TbsSkinStringGrid.Destroy;
  2259.   function FreeItem(TheIndex: Integer; TheItem: Pointer): Integer; far;
  2260.   begin
  2261.     TObject(TheItem).Free;
  2262.     Result := 0;
  2263.   end;
  2264. begin
  2265.   if FRows <> nil then
  2266.   begin
  2267.     TbsarseList(FRows).ForAll(@FreeItem);
  2268.     TbsarseList(FRows).Free;
  2269.   end;
  2270.   if FCols <> nil then
  2271.   begin
  2272.     TbsarseList(FCols).ForAll(@FreeItem);
  2273.     TbsarseList(FCols).Free;
  2274.   end;
  2275.   if FData <> nil then
  2276.   begin
  2277.     TbsarseList(FData).ForAll(@FreeItem);
  2278.     TbsarseList(FData).Free;
  2279.   end;
  2280.   inherited Destroy;
  2281. end;
  2282. procedure TbsSkinStringGrid.ColumnMoved(FromIndex, ToIndex: Longint);
  2283.   function MoveColData(Index: Integer; ARow: TStringSparseList): Integer; far;
  2284.   begin
  2285.     ARow.Move(FromIndex, ToIndex);
  2286.     Result := 0;
  2287.   end;
  2288. begin
  2289.   TbsarseList(FData).ForAll(@MoveColData);
  2290.   Invalidate;
  2291.   inherited ColumnMoved(FromIndex, ToIndex);
  2292. end;
  2293. procedure TbsSkinStringGrid.RowMoved(FromIndex, ToIndex: Longint);
  2294. begin
  2295.   TbsarseList(FData).Move(FromIndex, ToIndex);
  2296.   Invalidate;
  2297.   inherited RowMoved(FromIndex, ToIndex);
  2298. end;
  2299. function TbsSkinStringGrid.GetEditText(ACol, ARow: Longint): string;
  2300. begin
  2301.   Result := Cells[ACol, ARow];
  2302.   if Assigned(FOnGetEditText) then FOnGetEditText(Self, ACol, ARow, Result);
  2303. end;
  2304. procedure TbsSkinStringGrid.SetEditText(ACol, ARow: Longint; const Value: string);
  2305. begin
  2306.   DisableEditUpdate;
  2307.   try
  2308.     if Value <> Cells[ACol, ARow] then Cells[ACol, ARow] := Value;
  2309.   finally
  2310.     EnableEditUpdate;
  2311.   end;
  2312.   inherited SetEditText(ACol, ARow, Value);
  2313. end;
  2314. procedure TbsSkinStringGrid.DrawCell(ACol, ARow: Longint; ARect: TRect;
  2315.   AState: TGridDrawState);
  2316. var
  2317.   R: TRect;
  2318.   S: String;
  2319.   TX, TY: Integer;
  2320. begin
  2321.   if DefaultDrawing
  2322.   then
  2323.     with Canvas do
  2324.     begin
  2325.       R := GetNewTextRect(ARect, AState);
  2326.       Brush.Style := bsClear;
  2327.       S := Cells[ACol, ARow];
  2328.       TX := R.Left + 2;
  2329.       TY := R.Top + RectHeight(R) div 2 - TextHeight(S) div 2;
  2330.       TextRect(R, TX, TY, S);
  2331.       Brush.Style := bsSolid;
  2332.     end;
  2333.   inherited DrawCell(ACol, ARow, ARect, AState);
  2334. end;
  2335. procedure TbsSkinStringGrid.DisableEditUpdate;
  2336. begin
  2337.   Inc(FEditUpdate);
  2338. end;
  2339. procedure TbsSkinStringGrid.EnableEditUpdate;
  2340. begin
  2341.   Dec(FEditUpdate);
  2342. end;
  2343. procedure TbsSkinStringGrid.Initialize;
  2344. var
  2345.   quantum: TbsAQuantum;
  2346. begin
  2347.   if FCols = nil then
  2348.   begin
  2349.     if ColCount > 512 then quantum := SPALarge else quantum := SPASmall;
  2350.     FCols := TbsarseList.Create(quantum);
  2351.   end;
  2352.   if RowCount > 256 then quantum := SPALarge else quantum := SPASmall;
  2353.   if FRows = nil then FRows := TbsarseList.Create(quantum);
  2354.   if FData = nil then FData := TbsarseList.Create(quantum);
  2355. end;
  2356. procedure TbsSkinStringGrid.SetUpdateState(Updating: Boolean);
  2357. begin
  2358.   FUpdating := Updating;
  2359.   if not Updating and FNeedsUpdating then
  2360.   begin
  2361.     InvalidateGrid;
  2362.     FNeedsUpdating := False;
  2363.   end;
  2364. end;
  2365. procedure TbsSkinStringGrid.Update(ACol, ARow: Integer);
  2366. begin
  2367.   if not FUpdating then InvalidateCell(ACol, ARow)
  2368.   else FNeedsUpdating := True;
  2369.   if (ACol = Col) and (ARow = Row) and (FEditUpdate = 0) then InvalidateEditor;
  2370. end;
  2371. function  TbsSkinStringGrid.EnsureColRow(Index: Integer; IsCol: Boolean):
  2372.   TbsSkinStringGridStrings;
  2373. var
  2374.   RCIndex: Integer;
  2375.   PList: ^TbsarseList;
  2376. begin
  2377.   if IsCol then PList := @FCols else PList := @FRows;
  2378.   Result := TbsSkinStringGridStrings(PList^[Index]);
  2379.   if Result = nil then
  2380.   begin
  2381.     if IsCol then RCIndex := -Index - 1 else RCIndex := Index + 1;
  2382.     Result := TbsSkinStringGridStrings.Create(Self, RCIndex);
  2383.     PList^[Index] := Result;
  2384.   end;
  2385. end;
  2386. function  TbsSkinStringGrid.EnsureDataRow(ARow: Integer): Pointer;
  2387. var
  2388.   quantum: TbsAQuantum;
  2389. begin
  2390.   Result := TStringSparseList(TbsarseList(FData)[ARow]);
  2391.   if Result = nil then
  2392.   begin
  2393.     if ColCount > 512 then quantum := SPALarge else quantum := SPASmall;
  2394.     Result := TStringSparseList.Create(quantum);
  2395.     TbsarseList(FData)[ARow] := Result;
  2396.   end;
  2397. end;
  2398. function TbsSkinStringGrid.GetCells(ACol, ARow: Integer): string;
  2399. var
  2400.   ssl: TStringSparseList;
  2401. begin
  2402.   ssl := TStringSparseList(TbsarseList(FData)[ARow]);
  2403.   if ssl = nil then Result := '' else Result := ssl[ACol];
  2404. end;
  2405. function TbsSkinStringGrid.GetCols(Index: Integer): TStrings;
  2406. begin
  2407.   Result := EnsureColRow(Index, True);
  2408. end;
  2409. function TbsSkinStringGrid.GetObjects(ACol, ARow: Integer): TObject;
  2410. var
  2411.   ssl: TStringSparseList;
  2412. begin
  2413.   ssl := TStringSparseList(TbsarseList(FData)[ARow]);
  2414.   if ssl = nil then Result := nil else Result := ssl.Objects[ACol];
  2415. end;
  2416. function TbsSkinStringGrid.GetRows(Index: Integer): TStrings;
  2417. begin
  2418.   Result := EnsureColRow(Index, False);
  2419. end;
  2420. procedure TbsSkinStringGrid.SetCells(ACol, ARow: Integer; const Value: string);
  2421. begin
  2422.   TbsSkinStringGridStrings(EnsureDataRow(ARow))[ACol] := Value;
  2423.   EnsureColRow(ACol, True);
  2424.   EnsureColRow(ARow, False);
  2425.   Update(ACol, ARow);
  2426. end;
  2427. procedure TbsSkinStringGrid.SetCols(Index: Integer; Value: TStrings);
  2428. begin
  2429.   EnsureColRow(Index, True).Assign(Value);
  2430. end;
  2431. procedure TbsSkinStringGrid.SetObjects(ACol, ARow: Integer; Value: TObject);
  2432. begin
  2433.   TbsSkinStringGridStrings(EnsureDataRow(ARow)).Objects[ACol] := Value;
  2434.   EnsureColRow(ACol, True);
  2435.   EnsureColRow(ARow, False);
  2436.   Update(ACol, ARow);
  2437. end;
  2438. procedure TbsSkinStringGrid.SetRows(Index: Integer; Value: TStrings);
  2439. begin
  2440.   EnsureColRow(Index, False).Assign(Value);
  2441. end;
  2442. end.