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

Delphi控件源码

开发平台:

Delphi

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