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

Delphi控件源码

开发平台:

Delphi

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