git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.DevExpressVCL@38 05c56307-c608-d34a-929d-697000501d7a
1366 lines
45 KiB
ObjectPascal
1366 lines
45 KiB
ObjectPascal
{*******************************************************************}
|
|
{ }
|
|
{ Developer Express Cross platform Visual Component Library }
|
|
{ ExpressSpreadSheet }
|
|
{ }
|
|
{ Copyright (c) 2001-2009 Developer Express Inc. }
|
|
{ ALL RIGHTS RESERVED }
|
|
{ }
|
|
{ The entire contents of this file is protected by U.S. and }
|
|
{ International Copyright Laws. Unauthorized reproduction, }
|
|
{ reverse-engineering, and distribution of all or any portion of }
|
|
{ the code contained in this file is strictly prohibited and may }
|
|
{ result in severe civil and criminal penalties and will be }
|
|
{ prosecuted to the maximum extent possible under the law. }
|
|
{ }
|
|
{ RESTRICTIONS }
|
|
{ }
|
|
{ THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES }
|
|
{ (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE }
|
|
{ SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS }
|
|
{ LICENSED TO DISTRIBUTE THE EXPRESSSPREADSHEET AND ALL }
|
|
{ ACCOMPANYING VCL AND CLX CONTROLS AS PART OF AN EXECUTABLE }
|
|
{ PROGRAM ONLY. }
|
|
{ }
|
|
{ THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED }
|
|
{ FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE }
|
|
{ COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE }
|
|
{ AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT }
|
|
{ AND PERMISSION FROM DEVELOPER EXPRESS INC. }
|
|
{ }
|
|
{ CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON }
|
|
{ ADDITIONAL RESTRICTIONS. }
|
|
{ }
|
|
{*******************************************************************}
|
|
|
|
unit cxSSViewInfo;
|
|
|
|
{$I cxVer.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, Math, SysUtils,
|
|
Windows, Graphics, cxGraphics, cxExcelConst, cxSSTypes, cxSSStyles,
|
|
cxSSData, cxSSHeaders, cxSSFormulas, cxSSUtils, cxSSPainterWrapper;
|
|
|
|
type
|
|
TcxSSheetViewInfoClass = class of TcxSSheetViewInfo;
|
|
|
|
TcxSSNavigatorBtnStates = set of TcxSSNavigatorBtn;
|
|
|
|
TcxSSCaptionHitTest = (htNone, htCaption, htButton);
|
|
|
|
TcxSSCaptionStates = set of (csCurrent, csHidden);
|
|
|
|
TcxSSCaptionInfoBrick = record
|
|
State: TcxSSCaptionStates;
|
|
DisplayText: TcxString;
|
|
BoundsRect: TRect;
|
|
end;
|
|
|
|
TcxSSBookCaptionViewInfo = record
|
|
Bounds: TRect;
|
|
FillColor: TColor;
|
|
Font: TFont;
|
|
PageCount: Byte;
|
|
IsButtonDown: Boolean;
|
|
BtnDown: ShortInt;
|
|
Enabled: TcxSSNavigatorBtnStates;
|
|
FirstVisibleCaption: Integer;
|
|
BtnBricks: array[TcxSSNavigatorBtn] of TRect;
|
|
Bricks: array of TcxSSCaptionInfoBrick;
|
|
end;
|
|
|
|
PcxEdgeVertex = ^TcxEdgeVertex;
|
|
TcxEdgeVertex = packed array[0..2] of TRect;
|
|
|
|
PcxSSInfoBrick = ^TcxSSInfoBrick;
|
|
TcxSSInfoBrick = packed record
|
|
Col, Row : Integer;
|
|
DisplayRect : TRect;
|
|
TextRect : TRect;
|
|
BrushStyle : TcxSSFillStyle;
|
|
BrushBkColor : Word;
|
|
BrushFGColor : Word;
|
|
Edges: TcxSSBordersStyle;
|
|
TopEdgeVertex: TcxEdgeVertex;
|
|
LeftEdgeVertex: TcxEdgeVertex;
|
|
TextSettings : TcxTextParameters;
|
|
IsSelected: Boolean;
|
|
case IsMerge: Boolean of
|
|
True: (UnionIndex: Integer);
|
|
False: ();
|
|
end;
|
|
|
|
TcxSSUnionInfoBrick = packed record
|
|
UnionRange: TRect;
|
|
BrushStyle : TcxSSFillStyle;
|
|
BrushBkColor : Word;
|
|
BrushFGColor : Word;
|
|
TextRect : TRect;
|
|
DisplayRect : TRect;
|
|
IsSelected : Boolean;
|
|
TextSettings : TcxTextParameters;
|
|
end;
|
|
|
|
TcxSSViewInfoData = packed record
|
|
DisplayRect: TRect;
|
|
Bounds : TRect;
|
|
DrawHeaders: Boolean;
|
|
Selection : TRect;
|
|
SelectedBricks: TRect;
|
|
HeaderColor: Integer;
|
|
HeaderFontColor: Integer;
|
|
IsSelectionVisible: Boolean;
|
|
UnionBricks: array of TcxSSUnionInfoBrick;
|
|
Bricks : array of array of TcxSSInfoBrick;
|
|
end;
|
|
|
|
TcxSSHitTestState = (htCell, htRowHeader, htColHeader, htResize, htUpperLeft);
|
|
TcxSSHitTestStates = set of TcxSSHitTestState;
|
|
|
|
TcxSSheetViewInfo = class
|
|
private
|
|
FDrawGrid: Boolean;
|
|
FInfoData: TcxSSViewInfoData;
|
|
FInfoChanged: Boolean;
|
|
FOwner: TObject;
|
|
FShowFormulas: Boolean;
|
|
function GetBottomRow: Integer;
|
|
function GetBottomRight: TPoint;
|
|
function GetCanvas: TcxCanvasWrapper;
|
|
function GetClientRect: TRect;
|
|
function GetColHeaderHeight: Integer;
|
|
function GetDataStorage: TcxSSDataStorage;
|
|
function GetFloatDigits: Byte;
|
|
function GetFormulasCache: TcxSSFormulasCache;
|
|
function GetHeader(AType: TcxSSHeaderType): TcxSSHeader;
|
|
function GetLeftCol: Integer;
|
|
function GetMergeCellBorders(ACol, ARow: Integer): TcxSSEdgeBorders;
|
|
function GetRowHeaderWidth: Integer;
|
|
function GetRightCol: Integer;
|
|
function GetTopLeft: TPoint;
|
|
function GetTopRow: Integer;
|
|
procedure SetBottomRight(const Value: TPoint);
|
|
procedure SetBottomRow(const Value: Integer);
|
|
procedure SetInfoChanged(const Value: Boolean);
|
|
procedure SetLeftCol(const Value: Integer);
|
|
procedure SetRightCol(const Value: Integer);
|
|
procedure SetTopLeft(Value: TPoint);
|
|
procedure SetTopRow(const Value: Integer);
|
|
protected
|
|
function AddUnionBrick(ACol, ARow: Integer): Integer; virtual;
|
|
procedure CalculateViewInfo; virtual;
|
|
procedure CheckSelectionVisibility;
|
|
function ConvertCellRecToDisplayText(const ACellRec: TcxSSCellRec;
|
|
var AColor: Word): TcxString;
|
|
procedure InvalidateRect(ARect: TRect); virtual;
|
|
function GetCellIndexes(ACol, ARow: Integer; var AColPos, ARowPos: Integer): Boolean;
|
|
function GetCellsRectIndexes(const ASelRect: TRect): TRect;
|
|
function GetCellTextAlignment(const ACell: TcxSSCellRec): TcxHorzTextAlign;
|
|
procedure InvalidateBrick(AColPos, ARowPos: Integer); virtual;
|
|
procedure InvalidateCells; virtual;
|
|
procedure SetBrickInfo(AColPos, ARowPos: Integer); virtual;
|
|
property Canvas: TcxCanvasWrapper read GetCanvas;
|
|
property ClipRect: TRect read GetClientRect;
|
|
property DataStorage: TcxSSDataStorage read GetDataStorage;
|
|
property Headers[AType: TcxSSHeaderType]: TcxSSHeader read GetHeader;
|
|
property FormulasCache: TcxSSFormulasCache read GetFormulasCache;
|
|
property FloatDigits: Byte read GetFloatDigits;
|
|
public
|
|
constructor Create(AOwner: TObject); virtual;
|
|
procedure ClearBrickText(ACol, ARow: Integer);
|
|
function HitTest(X, Y: Integer; var ACol, ARow: Integer): TcxSSHitTestStates;
|
|
procedure UpdateCellInfo(const ACol, ARow: Integer);
|
|
procedure UpdateCellsInfo(const ACellRect: TRect);
|
|
procedure UpdateOnChangeSelection;
|
|
procedure UpdateOnCancelMode;
|
|
procedure UpdateOnResize;
|
|
procedure UpdateOnResizeColumn(const ACol: Integer);
|
|
procedure UpdateOnResizeRow(const ARow: Integer);
|
|
procedure UpdateViewInfo;
|
|
property BottomRight: TPoint read GetBottomRight write SetBottomRight;
|
|
property BottomRow: Integer read GetBottomRow write SetBottomRow;
|
|
property ColHeaderHeight: Integer read GetColHeaderHeight;
|
|
property InfoData: TcxSSViewInfoData read FInfoData;
|
|
property InfoChanged: Boolean read FInfoChanged write SetInfoChanged;
|
|
property LeftCol: Integer read GetLeftCol write SetLeftCol;
|
|
property Owner: TObject read FOwner;
|
|
property RightCol: Integer read GetRightCol write SetRightCol;
|
|
property RowHeaderWidth: Integer read GetRowHeaderWidth;
|
|
property TopLeft: TPoint read GetTopLeft write SetTopLeft;
|
|
property TopRow: Integer read GetTopRow write SetTopRow;
|
|
end;
|
|
|
|
implementation
|
|
uses
|
|
cxSSheet;
|
|
|
|
type
|
|
TcxDataStorageAccess = class(TcxSSDataStorage);
|
|
TcxHeaderAccess = class(TcxSSHeader);
|
|
TcxBookAccess = class(TcxCustomSpreadSheetBook);
|
|
TcxBookSubControlAccess = class(TcxWorkBookSubControl);
|
|
TcxSheetAccess = class(TcxSSBookSheet);
|
|
TcxListenerAccess = class(TcxSSListener);
|
|
|
|
{ TcxSSViewInfo }
|
|
constructor TcxSSheetViewInfo.Create(AOwner: TObject);
|
|
begin
|
|
FOwner := AOwner;
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.ClearBrickText(ACol, ARow: Integer);
|
|
begin
|
|
with FInfoData do
|
|
begin
|
|
if not Bricks[ACol, ARow].IsMerge then
|
|
SetLength(Bricks[ACol, ARow].TextSettings.TextBricks, 0)
|
|
else
|
|
SetLength(UnionBricks[Bricks[ACol, ARow].UnionIndex].TextSettings.TextBricks, 0);
|
|
end;
|
|
InvalidateBrick(ACol, ARow)
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.HitTest(X, Y: Integer; var ACol, ARow: Integer): TcxSSHitTestStates;
|
|
var
|
|
I, J: Integer;
|
|
begin
|
|
Result := [];
|
|
with FInfoData do
|
|
begin
|
|
X := TcxSSUtils.CheckValue(X, 0, ClipRect.Right);
|
|
Y := TcxSSUtils.CheckValue(Y, 0, ClipRect.Bottom);
|
|
for I := Length(Bricks) - 1 downto 0 do
|
|
if (X >= Bricks[I, 0].DisplayRect.Left) and (X < Bricks[I, 0].DisplayRect.Right) then
|
|
begin
|
|
for J := Length(Bricks[0]) - 1 downto 0 do
|
|
if (Y >= FInfoData.Bricks[0, J].DisplayRect.Top) and
|
|
(Y < FInfoData.Bricks[0, J].DisplayRect.Bottom) then
|
|
begin
|
|
ACol := I - 1;
|
|
ARow := J - 1;;
|
|
if (ACol >= 0) and (ARow >= 0)then
|
|
begin
|
|
ACol := FInfoData.Bricks[I, J].Col;
|
|
ARow := FInfoData.Bricks[I, J].Row;
|
|
Include(Result, htCell)
|
|
end
|
|
else
|
|
begin
|
|
if (ACol < 0) and (ARow < 0) then
|
|
Include(Result, htUpperLeft)
|
|
else
|
|
if ACol < 0 then
|
|
begin
|
|
Include(Result, htRowHeader);
|
|
ARow := Bricks[0, J].Row;
|
|
if (ARow > 0) and (((Y + 4) >= Bricks[0, J].DisplayRect.Top) and
|
|
((Y - 4) <= Bricks[0, J].DisplayRect.Top)) then
|
|
begin
|
|
Include(Result, htResize);
|
|
if ARow > 0 then Dec(ARow);
|
|
end;
|
|
while (ARow > 0) and not Headers[htRow].Visible[ARow] do Dec(ARow);
|
|
end
|
|
else
|
|
if ARow < 0 then
|
|
begin
|
|
Include(Result, htColHeader);
|
|
ACol := Bricks[I, 0].Col;
|
|
if (ACol > 0) and ((X + 4) > Bricks[I, 0].DisplayRect.Left) and
|
|
((X - 4) < Bricks[I, 0].DisplayRect.Left) then
|
|
begin
|
|
Include(Result, htResize);
|
|
Dec(ACol);
|
|
end;
|
|
while (ACol > 0) and not Headers[htCol].Visible[ACol] do Dec(ACol);
|
|
end;
|
|
end;
|
|
Break;
|
|
end;
|
|
if Result <> [] then Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.UpdateCellInfo(const ACol, ARow: Integer);
|
|
begin
|
|
CalculateViewInfo;
|
|
with FInfoData.DisplayRect do
|
|
InvalidateRect(Rect(RowHeaderWidth, ColHeaderHeight, Right, Bottom));
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.UpdateCellsInfo(const ACellRect: TRect);
|
|
begin
|
|
UpdateViewInfo;
|
|
Exit;
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.UpdateOnResize;
|
|
var
|
|
AOldRect, ARect: TRect;
|
|
begin
|
|
AOldRect := FInfoData.DisplayRect;
|
|
try
|
|
CalculateViewInfo;
|
|
finally
|
|
ARect := FInfoData.DisplayRect;
|
|
if not EqualRect(AOldRect, ARect) then
|
|
begin
|
|
if AOldRect.Right < ARect.Right then
|
|
begin
|
|
ARect.TopLeft := Point(AOldRect.Right, 0);
|
|
InflateRect(ARect, 2, 2);
|
|
InvalidateRect(ARect);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.UpdateOnCancelMode;
|
|
var
|
|
I, J: Integer;
|
|
begin
|
|
for I := 0 to Length(FInfoData.Bricks) - 1 do
|
|
for J := 0 to Length(FInfoData.Bricks[0]) - 1 do
|
|
with FInfoData.Bricks[I, J] do
|
|
if IsSelected then
|
|
InvalidateRect(DisplayRect);
|
|
for I := 0 to Length(FInfoData.UnionBricks) - 1 do
|
|
with FInfoData.UnionBricks[I] do
|
|
if IsSelected then
|
|
InvalidateRect(DisplayRect);
|
|
with FInfoData.SelectedBricks do
|
|
if (Left >= 1) and (Left < Length(FInfoData.Bricks)) and
|
|
(Top >= 1) and (Top < Length(FInfoData.Bricks[0])) then
|
|
if FInfoData.Bricks[Left, Top].IsMerge then
|
|
InvalidateRect(FInfoData.UnionBricks[FInfoData.Bricks[Left, Top].UnionIndex].DisplayRect)
|
|
else
|
|
InvalidateRect(FInfoData.Bricks[Left, Top].DisplayRect);
|
|
CheckSelectionVisibility;
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.UpdateOnResizeColumn(const ACol: Integer);
|
|
var
|
|
I, J, K: Integer;
|
|
R, ARect: TRect;
|
|
|
|
begin
|
|
K := 0;
|
|
if (Length(FInfoData.Bricks) = 0) or (Length(FInfoData.Bricks[0]) = 0) then Exit;
|
|
while (K < Length(FInfoData.Bricks)) and (FInfoData.Bricks[K, 0].Col < ACol) do Inc(K);
|
|
if K >= Length(FInfoData.Bricks) then Exit;
|
|
for I := 0 to Length(FInfoData.Bricks[K]) - 1 do
|
|
InvalidateBrick(K, I);
|
|
CalculateViewInfo;
|
|
K := 0;
|
|
while FInfoData.Bricks[K, 0].Col < ACol do Inc(K);
|
|
ARect.TopLeft := Point(FInfoData.Bricks[K, 0].DisplayRect.Left, 0);
|
|
ARect.BottomRight := FInfoData.DisplayRect.BottomRight;
|
|
for I := 0 to Length(FInfoData.Bricks[K]) - 1 do
|
|
InvalidateBrick(K, I);
|
|
InvalidateRect(ARect);
|
|
for I := 0 to K do
|
|
for J := 0 to Length(FInfoData.Bricks[I]) - 1 do
|
|
if IntersectRect(R, ARect, FInfoData.Bricks[I, J].TextRect) then
|
|
InvalidateBrick(I, J);
|
|
for I := 0 to Length(FInfoData.UnionBricks) - 1 do
|
|
if IntersectRect(R, FInfoData.UnionBricks[I].TextRect, ARect) then
|
|
InvalidateRect(FInfoData.UnionBricks[I].TextRect);
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.UpdateOnResizeRow(const ARow: Integer);
|
|
var
|
|
K: Integer;
|
|
R, ARect: TRect;
|
|
begin
|
|
with FInfoData do
|
|
if (Length(Bricks) = 0) or (Length(Bricks[0]) = 0) then Exit;
|
|
CalculateViewInfo;
|
|
K := 0;
|
|
while (K < Length(FInfoData.Bricks[0])) and (FInfoData.Bricks[0, K].Row < ARow) do Inc(K);
|
|
if K >= Length(FInfoData.Bricks[0]) then Exit;
|
|
ARect.TopLeft := Point(0, FInfoData.Bricks[0, K].DisplayRect.Top);
|
|
ARect.BottomRight := FInfoData.DisplayRect.BottomRight;
|
|
InvalidateRect(ARect);
|
|
for K := 0 to Length(FInfoData.UnionBricks) - 1 do
|
|
if IntersectRect(R, FInfoData.UnionBricks[K].TextRect, ARect) then
|
|
InvalidateRect(FInfoData.UnionBricks[K].TextRect);
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.UpdateViewInfo;
|
|
begin
|
|
try
|
|
CalculateViewInfo;
|
|
CheckSelectionVisibility;
|
|
finally
|
|
(FOwner as TcxSSBookSheet).Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.UpdateOnChangeSelection;
|
|
|
|
procedure UpdateBrickSelection(ABrickCol, ABrickRow: Integer; AIsSelected: Boolean);
|
|
var
|
|
K, L: Integer;
|
|
ARange: TRect;
|
|
begin
|
|
with FInfoData do
|
|
begin
|
|
if Bricks[ABrickCol, ABrickRow].IsMerge then
|
|
begin
|
|
with UnionBricks[Bricks[ABrickCol, ABrickRow].UnionIndex] do
|
|
begin
|
|
ARange := GetCellsRectIndexes(UnionRange);
|
|
for K := ARange.Left to ARange.Right do
|
|
for L := ARange.Top to ARange.Bottom do
|
|
if ((K > 0) and (K < Length(Bricks))) and
|
|
((L > 0) and (L < Length(Bricks[0]))) then
|
|
begin
|
|
FInfoData.Bricks[K, L].IsSelected := AIsSelected;
|
|
InvalidateRect(FInfoData.Bricks[K, L].DisplayRect);
|
|
end;
|
|
IsSelected := AIsSelected;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Bricks[ABrickCol, ABrickRow].IsSelected := AIsSelected;
|
|
InvalidateRect(Bricks[ABrickCol, ABrickRow].DisplayRect);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
AOldRect, ANewRect: TRect;
|
|
I, J: Integer;
|
|
ACol, ARow: Integer;
|
|
IsCurrent, IsColSelected, IsRowSelected: Boolean;
|
|
ASelected: Boolean;
|
|
|
|
begin
|
|
InfoChanged := True;
|
|
ANewRect := DataStorage.Selection;
|
|
AOldRect := FInfoData.Selection;
|
|
if EqualRect(AOldRect, ANewRect) then Exit;
|
|
FInfoData.Selection := ANewRect;
|
|
for I := 1 to Length(FInfoData.Bricks) - 1 do
|
|
begin
|
|
ACol := FInfoData.Bricks[I, 0].Col;
|
|
IsColSelected := (ACol >= ANewRect.Left) and (ACol <= ANewRect.Right);
|
|
if FInfoData.Bricks[I, 0].IsSelected <> IsColSelected then
|
|
begin
|
|
FInfoData.Bricks[I, 0].IsSelected := IsColSelected;
|
|
InvalidateRect(FInfoData.Bricks[I, 0].DisplayRect);
|
|
end;
|
|
for J := 1 to Length(FInfoData.Bricks[0]) - 1 do
|
|
begin
|
|
ARow := FInfoData.Bricks[0, J].Row;
|
|
IsRowSelected := (ARow >= ANewRect.Top) and (ARow <= ANewRect.Bottom);
|
|
if FInfoData.Bricks[0, J].IsSelected <> IsRowSelected then
|
|
begin
|
|
FInfoData.Bricks[0, J].IsSelected := IsRowSelected;
|
|
InvalidateRect(FInfoData.Bricks[0, J].DisplayRect);
|
|
end;
|
|
with FInfoData.Bricks[I, J] do
|
|
begin
|
|
IsCurrent := (ACol = ANewRect.Left) and (ARow = ANewRect.Top);
|
|
ASelected := IsColSelected and IsRowSelected and not IsCurrent;
|
|
with FInfoData.SelectedBricks do
|
|
if IsCurrent and ((I <> Left) or (J <> Top)) then
|
|
begin
|
|
UpdateBrickSelection(I, J, not IsCurrent);
|
|
if (Left >= 0) and (Left <= Length(FInfoData.Bricks)) and
|
|
(Top >= 0) and (Top <= Length(FinfoData.Bricks[0])) then
|
|
UpdateBrickSelection(Left, Top, FinfoData.Bricks[Left, Top].IsSelected);
|
|
end;
|
|
if (ASelected <> IsSelected) then
|
|
begin
|
|
if not FInfoData.Bricks[I, J].IsMerge then
|
|
UpdateBrickSelection(I, J, ASelected)
|
|
else
|
|
with FInfoData.UnionBricks[FInfoData.Bricks[I, J].UnionIndex] do
|
|
if (UnionRange.Top <> ANewRect.Top) or (UnionRange.Left <> ANewRect.Left) then
|
|
UpdateBrickSelection(I, J, ASelected);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
FInfoData.SelectedBricks := GetCellsRectIndexes(FInfoData.Selection);
|
|
CheckSelectionVisibility;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.AddUnionBrick(ACol, ARow: Integer): Integer;
|
|
|
|
function IndexOfRect(const ARect: TRect): Integer;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := -1;
|
|
with FInfoData do
|
|
begin
|
|
for I := 0 to Length(UnionBricks) - 1 do
|
|
if EqualRect(UnionBricks[I].UnionRange, ARect) then
|
|
begin
|
|
Result := I;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
ARect: TRect;
|
|
ACellRec: TcxSSCellRec;
|
|
AHorzAlign: TcxHorzTextAlign;
|
|
AText: TcxString;
|
|
AColor: Word;
|
|
begin
|
|
Result := -1;
|
|
if not DataStorage.CheckInMergeRange(Point(ACol, ARow), ARect) then Exit;
|
|
Result := IndexOfRect(ARect);
|
|
begin
|
|
if Result = -1 then
|
|
begin
|
|
Result := Length(FInfoData.UnionBricks);
|
|
SetLength(FInfoData.UnionBricks, Result + 1);
|
|
with FInfoData.UnionBricks[Result] do
|
|
begin
|
|
ACellRec := DataStorage[ARect.Left, ARect.Top];
|
|
AColor := 0;
|
|
AText := ConvertCellRecToDisplayText(ACellRec, AColor);
|
|
IsSelected := (Int64(ARect.TopLeft) <> Int64(FInfoData.Selection.TopLeft)) and
|
|
(TcxSSUtils.PointInRect(FInfoData.Selection, ARect.TopLeft) and
|
|
TcxSSUtils.PointInRect(FInfoData.Selection, ARect.BottomRight));
|
|
DisplayRect := DataStorage.CellRect(FInfoData.Bricks[1, 0].Col,
|
|
FInfoData.Bricks[0, 1].Row, ACol, ARow, True);
|
|
UnionRange := ARect;
|
|
BrushStyle := ACellRec.StylePtr^.BrushStyle;
|
|
BrushBkColor := ACellRec.StylePtr^.BrushBkColor;
|
|
BrushFgColor := ACellRec.StylePtr^.BrushFgColor;
|
|
OffsetRect(DisplayRect, RowHeaderWidth, ColHeaderHeight);
|
|
TextRect := DisplayRect;
|
|
InflateRect(TextRect, -1, 0);
|
|
OffsetRect(TextRect, 0, 2);
|
|
Dec(TextRect.Right, 2);
|
|
Dec(TextRect.Bottom, 2);
|
|
AHorzALign := GetCellTextAlignment(ACellRec);
|
|
with ACellRec.StylePtr^ do
|
|
begin
|
|
if AColor = 0 then
|
|
TextSettings.FontColor := FontPtr^.FontColor
|
|
else
|
|
TextSettings.FontColor := AColor;
|
|
if (ACellRec.StylePtr^.FormatIndex <> $31) and cxTryStrToFloat(AText) then
|
|
AText := TcxSSUtils.FormatText(StrToFloat(AText),
|
|
ACellRec.StylePtr^.FormatIndex, FloatDigits, TextSettings.FontColor);
|
|
Canvas.SelectFont(FontPtr^.FontHandle);
|
|
Canvas.CalculateTextExtents(AText, TextRect, AHorzAlign, VertAlign,
|
|
WordBreak, TextSettings);
|
|
end;
|
|
if DisplayRect.Top < ColHeaderHeight then
|
|
begin
|
|
DisplayRect.Top := ColHeaderHeight;
|
|
TextRect.Top := ColHeaderHeight;
|
|
end;
|
|
if DisplayRect.Left < RowHeaderWidth then
|
|
begin
|
|
DisplayRect.Left := RowHeaderWidth;
|
|
TextRect.Left := RowHeaderWidth;
|
|
end;
|
|
Inc(TextRect.Right);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.CalculateViewInfo;
|
|
|
|
procedure GetHeaderBrickInfo(var ABrick: TcxSSInfoBrick; ACol, ARow: Integer;
|
|
const ADisplayRect: TRect; const ADisplayText: TcxString; IsColHeader: Boolean = True);
|
|
begin
|
|
with ABrick do
|
|
begin
|
|
Col := ACol;
|
|
Row := ARow;
|
|
DisplayRect := ADisplayRect;
|
|
Canvas.SetSingleText(ADisplayText, ADisplayRect, TextSettings, IsColHeader);
|
|
Inc(TextSettings.TextBricks[0].YPos, 2);
|
|
TextSettings.FontColor := $FFFF;
|
|
with FInfoData.Selection do
|
|
if IsColHeader then
|
|
IsSelected := (ACol >= Left) and (ACol <= Right)
|
|
else
|
|
IsSelected := (ARow >= Top) and (ARow <= Bottom);
|
|
end;
|
|
end;
|
|
|
|
function GetItemsCount(AFrom, ASize: Integer; AType: TcxSSHeaderType): Integer;
|
|
var
|
|
AItem, AItemsSize, AItemSize: Integer;
|
|
AHeader: TcxSSHeader;
|
|
begin
|
|
AHeader := Headers[AType];
|
|
Result := 0;
|
|
AItem := AFrom;
|
|
AItemsSize := 0;
|
|
while ASize > AItemsSize do
|
|
begin
|
|
AItemSize := AHeader[AItem];
|
|
if AItemSize > 0 then Inc(Result);
|
|
Inc(AItemsSize, AItemSize);
|
|
Inc(AItem);
|
|
end;
|
|
end;
|
|
|
|
procedure CheckTextExtentAndCorrectBorders(ARow: Integer);
|
|
|
|
procedure ClearBorder(J: Integer);
|
|
begin
|
|
FInfoData.Bricks[J, ARow].Edges[eLeft].Style := lsNone;
|
|
if (J - 1) > 0 then
|
|
FInfoData.Bricks[J - 1, ARow].Edges[eRight].Style := lsNone;
|
|
end;
|
|
|
|
function BrickIsEmpty(J: Integer): Boolean;
|
|
begin
|
|
with FInfoData.Bricks[J, ARow] do
|
|
Result := (Length(TextSettings.TextBricks) = 0) and (not IsMerge);
|
|
end;
|
|
|
|
var
|
|
I, J: Integer;
|
|
begin
|
|
for I := Length(FInfoData.Bricks) - 1 downto 1 do
|
|
with FInfoData.Bricks[I, ARow] do
|
|
if not IsMerge and not BrickIsEmpty(I) and not TextSettings.WordBreak then
|
|
begin
|
|
if TextRect.Left < DisplayRect.Left then
|
|
for J := I - 1 downto 1 do
|
|
if BrickIsEmpty(J) then
|
|
begin
|
|
ClearBorder(J + 1);
|
|
if TextRect.Left > FInfoData.Bricks[J, ARow].DisplayRect.Left then
|
|
Break;
|
|
end
|
|
else
|
|
begin
|
|
TextRect.Left := FInfoData.Bricks[J, ARow].DisplayRect.Right + 2;
|
|
Break;
|
|
end;
|
|
if TextRect.Left < FInfoData.Bricks[0, ARow].DisplayRect.Right then
|
|
TextRect.Left := FInfoData.Bricks[0, ARow].DisplayRect.Right;
|
|
|
|
if TextRect.Right > DisplayRect.Right then
|
|
for J := I + 1 to Length(FInfoData.Bricks) - 1 do
|
|
if BrickIsEmpty(J) then
|
|
begin
|
|
ClearBorder(J);
|
|
if TextRect.Right < FInfoData.Bricks[J, ARow].DisplayRect.Right then
|
|
Break;
|
|
end
|
|
else
|
|
begin
|
|
TextRect.Right := FInfoData.Bricks[J, ARow].DisplayRect.Left - 2;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure CorrectBordersAndDisplayRect(AColPos, ARowPos: Integer);
|
|
var
|
|
ATopLeft, ALeftTop: PcxEdgeVertex;
|
|
ASide: TcxSSEdgeBorder;
|
|
const
|
|
cxHalfWidth: array[TcxSSEdgeLineStyle] of Byte =
|
|
(1, 1, 2, 1, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 0);
|
|
DxDy: array[Boolean] of Byte = (1, 2);
|
|
|
|
begin
|
|
with FInfoData.Bricks[AColPos, ARowPos] do
|
|
begin
|
|
if (ARowPos > 1) and
|
|
(FInfoData.Bricks[AColPos, ARowPos - 1].Edges[eLeft].Style = lsDouble) then
|
|
ATopLeft := @FInfoData.Bricks[AColPos, ARowPos - 1].LeftEdgeVertex
|
|
else
|
|
ATopLeft := nil;
|
|
if (AColPos > 1) and
|
|
(FInfoData.Bricks[AColPos - 1, ARowPos].Edges[eTop].Style = lsDouble) then
|
|
ALeftTop := @FInfoData.Bricks[AColPos - 1, ARowPos].TopEdgeVertex
|
|
else
|
|
ALeftTop := nil;
|
|
for ASide := eLeft to eBottom do
|
|
if Edges[ASide].Style <> lsNone then
|
|
begin
|
|
if ASide in [eLeft, eTop] then
|
|
Inc(PIntArray(@DisplayRect)^[Byte(ASide)],
|
|
cxHalfWidth[Edges[ASide].Style])
|
|
else
|
|
Dec(PIntArray(@DisplayRect)^[Byte(ASide)],
|
|
DxDy[cxLineWidth[Edges[ASide].Style] = 3]);
|
|
end;
|
|
if Edges[eTop].Style = lsDouble then
|
|
begin
|
|
if Edges[eLeft].Style = lsDouble then
|
|
begin
|
|
Inc(TopEdgeVertex[2].Left);
|
|
Inc(LeftEdgeVertex[2].Top);
|
|
if ATopLeft = ALeftTop then
|
|
begin
|
|
Dec(TopEdgeVertex[0].Left);
|
|
Dec(LeftEdgeVertex[0].Top);
|
|
end
|
|
else
|
|
if ATopLeft <> nil then
|
|
begin
|
|
Inc(TopEdgeVertex[0].Left);
|
|
Dec(ATopLeft^[2].Bottom);
|
|
end
|
|
end
|
|
else
|
|
begin
|
|
if ATopLeft <> ALeftTop then
|
|
begin
|
|
Inc(TopEdgeVertex[0].Left);
|
|
if ATopLeft <> nil then
|
|
begin
|
|
Dec(TopEdgeVertex[2].Left);
|
|
Inc(ATopLeft[0].Bottom);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
if Edges[eLeft].Style = lsDouble then
|
|
begin
|
|
if ALeftTop <> nil then
|
|
begin
|
|
Inc(LeftEdgeVertex[0].Top);
|
|
if (Edges[eTop].Style <> lsDouble) and (ATopLeft = nil) then
|
|
begin
|
|
Inc(ALeftTop^[0].Right);
|
|
Dec(LeftEdgeVertex[2].Top);
|
|
end;
|
|
end
|
|
end
|
|
else
|
|
if (ALeftTop <> nil) and (ATopLeft <> nil) and
|
|
(Edges[eTop].Style <> lsDouble) then
|
|
begin
|
|
Inc(ALeftTop^[2].Right);
|
|
Inc(ATopLeft^[2].Bottom, 2);
|
|
end;
|
|
if Edges[eTop].Style = lsThick then
|
|
begin
|
|
Dec(TopEdgeVertex[0].Left);
|
|
Inc(TopEdgeVertex[0].Right, 2);
|
|
end;
|
|
if Edges[eLeft].Style = lsThick then
|
|
begin
|
|
Dec(LeftEdgeVertex[0].Top);
|
|
Inc(LeftEdgeVertex[0].Bottom, 2);
|
|
end
|
|
end;
|
|
end;
|
|
|
|
var
|
|
I, J, K: Integer;
|
|
ASize: TcxSSSize;
|
|
ATop, ALeft: Integer;
|
|
ARowCount, AColCount: Integer;
|
|
AColHeaderHeight: Integer;
|
|
ARowHeaderWidth: Integer;
|
|
ARange: TRect;
|
|
|
|
begin
|
|
InfoChanged := True;
|
|
if not TcxSheetAccess(Owner).Owner.HandleAllocated then Exit;
|
|
CheckSelectionVisibility;
|
|
Canvas.BeginPaint(TcxSheetAccess(Owner).ControlCanvas);
|
|
FShowFormulas := TcxSheetAccess(Owner).ShowFormulas;
|
|
FInfoData.DisplayRect := ClipRect;
|
|
AColHeaderHeight := ColHeaderHeight;
|
|
ARowHeaderWidth := RowHeaderWidth;
|
|
FDrawGrid := TcxSheetAccess(Owner).ShowGrid;
|
|
AColCount := GetItemsCount(FInfoData.Bounds.Left,
|
|
ClipRect.Right - ARowHeaderWidth, htCol) + 2;
|
|
ARowCount := GetItemsCount(FInfoData.Bounds.Top,
|
|
ClipRect.Bottom - AColHeaderHeight, htRow) + 2;
|
|
SetLength(FInfoData.UnionBricks, 0);
|
|
SetLength(FInfoData.Bricks, AColCount, ARowCount);
|
|
FInfoData.Selection := DataStorage.Selection;
|
|
FInfoData.DrawHeaders := TcxSheetAccess(Owner).ShowHeaders;
|
|
I := 1;
|
|
J := FInfoData.Bounds.Top;
|
|
ATop := AColHeaderHeight;
|
|
with TcxBookAccess(TcxBookSubControlAccess(Owner).Owner) do
|
|
begin
|
|
FInfoData.HeaderColor := Canvas.GetNativeColor(HeaderColor);
|
|
FInfoData.HeaderFontColor := Canvas.GetNativeColor(HeaderFont.Color);
|
|
Canvas.SelectFont(HeaderFont);
|
|
end;
|
|
while I < ARowCount do
|
|
begin
|
|
ASize := Headers[htRow][J];
|
|
Inc(J);
|
|
if ASize > 0 then
|
|
begin
|
|
GetHeaderBrickInfo(FInfoData.Bricks[0, I], 0, J - 1,
|
|
Rect(0, ATop, ARowHeaderWidth, ATop + ASize), IntToStr(J), False);
|
|
Inc(ATop, ASize);
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
I := 1;
|
|
J := FInfoData.Bounds.Left;
|
|
ALeft := ARowHeaderWidth;
|
|
while I < AColCount do
|
|
begin
|
|
ASize := Headers[htCol][J];
|
|
Inc(J);
|
|
if ASize > 0 then
|
|
begin
|
|
GetHeaderBrickInfo(FInfoData.Bricks[I, 0], J - 1, 0,
|
|
Rect(ALeft, 0, ALeft + ASize, AColHeaderHeight),
|
|
TcxSSUtils.ColumnNameByIndex(J - 1,
|
|
TcxSheetAccess(Owner).Owner.R1C1ReferenceStyle));
|
|
Inc(ALeft, ASize);
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
FInfoData.Bounds.BottomRight := Point(AColCount, ARowCount);
|
|
GetHeaderBrickInfo(FInfoData.Bricks[0, 0], -1, -1,
|
|
Rect(0, 0, ARowHeaderWidth, AColHeaderHeight), '');
|
|
for I := 1 to ARowCount - 1 do
|
|
begin
|
|
for J := 1 to AColCount - 1 do SetBrickInfo(J, I);
|
|
CheckTextExtentAndCorrectBorders(I);
|
|
end;
|
|
for I := 0 to Length(FInfoData.UnionBricks) - 1 do
|
|
begin
|
|
with FInfoData.UnionBricks[I] do
|
|
begin
|
|
ARange := GetCellsRectIndexes(UnionRange);
|
|
for J := ARange.Left to ARange.Right do
|
|
for K := ARange.Top to ARange.Bottom do
|
|
if ((J >= 1) and (J < Length(FInfoData.Bricks))) and
|
|
((K >= 1) and (K < Length(FInfoData.Bricks[0]))) then
|
|
with FInfoData.Bricks[J, K] do
|
|
begin
|
|
if J > ARange.Left then Edges[eLeft].Style := lsNone;
|
|
if J < ARange.Right then Edges[eRight].Style := lsNone;
|
|
if K > ARange.Top then Edges[eTop].Style := lsNone;
|
|
if K < ARange.Bottom then Edges[eBottom].Style := lsNone;
|
|
end;
|
|
end;
|
|
end;
|
|
for I := 1 to AColCount - 1 do
|
|
for J := 1 to ARowCount - 1 do CorrectBordersAndDisplayRect(I, J);
|
|
FInfoData.SelectedBricks := GetCellsRectIndexes(FInfoData.Selection);
|
|
TcxSheetAccess(Owner).InitScrollBars;
|
|
CheckSelectionVisibility;
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.CheckSelectionVisibility;
|
|
var
|
|
R: TRect;
|
|
begin
|
|
with InfoData do
|
|
begin
|
|
R := DataStorage.Selection;
|
|
IsSelectionVisible := (R.Left <= (RightCol + 1)) and (R.Right >= LeftCol) and
|
|
(R.Top <= (BottomRow + 1)) and (R.Bottom >= TopRow);
|
|
end;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.ConvertCellRecToDisplayText(
|
|
const ACellRec: TcxSSCellRec; var AColor: Word): TcxString;
|
|
var
|
|
AValue: Double;
|
|
begin
|
|
AValue := 0;
|
|
case ACellRec.DataType of
|
|
dtFunction:
|
|
Result := FormulasCache.FuncRecToDisplayText(ACellRec.FuncRecPtr, AColor);
|
|
dtDateTime:
|
|
Result := TcxSSUtils.FormatText(ACellRec.DateTime,
|
|
ACellRec.StylePtr^.FormatIndex, FloatDigits, AColor);
|
|
else
|
|
if (ACellRec.StylePtr^.FormatIndex <> $31) and cxTryStrToFloat(ACellRec.Text, AValue) then
|
|
Result := TcxSSUtils.FormatText(AValue, ACellRec.StylePtr^.FormatIndex,
|
|
FloatDigits, AColor)
|
|
else
|
|
Result := ACellRec.Text;
|
|
end;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetCellIndexes(ACol, ARow: Integer;
|
|
var AColPos, ARowPos: Integer): Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
I := 1;
|
|
while (I < Length(FInfoData.Bricks)) and (FInfoData.Bricks[I, 0].Col < ACol) do Inc(I);
|
|
Result := (I < Length(FInfoData.Bricks)) and (FInfoData.Bricks[I, 0].Col >= ACol);
|
|
if Result then
|
|
AColPos := I
|
|
else
|
|
AColPos := -1;
|
|
I := 1;
|
|
while (I < Length(FInfoData.Bricks[0])) and (FInfoData.Bricks[0, I].Row < ARow) do Inc(I);
|
|
if (I < Length(FInfoData.Bricks[0])) and (FInfoData.Bricks[0, I].Row >= ARow) then
|
|
ARowPos := I
|
|
else
|
|
ARowPos := -1;
|
|
Result := Result or (ARowPos <> -1);
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.InvalidateRect(ARect: TRect);
|
|
begin
|
|
InflateRect(ARect, 5, 5);
|
|
TcxBookSubControlAccess(FOwner).InvalidateRect(ARect);
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetCellsRectIndexes(const ASelRect: TRect): TRect;
|
|
var
|
|
I: Integer;
|
|
ARect: TRect;
|
|
begin
|
|
Result := Rect(-1, -1, -1, -1);
|
|
if Length(FInfoData.Bricks) = 0 then Exit;
|
|
with FInfoData do
|
|
ARect := Rect(Bricks[1, 1].Col, Bricks[1, 1].Row,
|
|
Bricks[Length(Bricks) - 1, 1].Col, Bricks[1, Length(Bricks[0]) - 1].Row);
|
|
|
|
if (ASelRect.Left >= ARect.Left) and (ASelRect.Left <= ARect.Right) then
|
|
for I := 1 to Length(FInfoData.Bricks) - 1 do
|
|
if FInfoData.Bricks[I, 0].Col > ASelRect.Left then
|
|
begin
|
|
if (Result.Left >=0) and (FInfoData.Bricks[Result.Left, 0].Col <> ASelRect.Left) then
|
|
Result.Left := I;
|
|
Break;
|
|
end
|
|
else
|
|
Result.Left := I;
|
|
|
|
if (ASelRect.Right >= ARect.Left) and (ASelRect.Right <= ARect.Right) then
|
|
for I := 1 to Length(FInfoData.Bricks) - 1 do
|
|
if FInfoData.Bricks[I, 0].Col > ASelRect.Right then
|
|
Break
|
|
else
|
|
Result.Right := I;
|
|
|
|
if (ASelRect.Top >= ARect.Top) and (ASelRect.Top <= ARect.Bottom) then
|
|
for I := 1 to Length(FInfoData.Bricks[0]) - 1 do
|
|
if FInfoData.Bricks[0, I].Row > ASelRect.Top then
|
|
begin
|
|
if FInfoData.Bricks[0, Result.Top].Row <> ASelRect.Top then
|
|
Result.Top := I;
|
|
Break;
|
|
end
|
|
else
|
|
Result.Top := I;
|
|
|
|
if (ASelRect.Bottom >= ARect.Top) and (ASelRect.Bottom <= ARect.Bottom) then
|
|
for I := 1 to Length(FInfoData.Bricks[0]) - 1 do
|
|
if FInfoData.Bricks[0, I].Row > ASelRect.Bottom then
|
|
Break
|
|
else
|
|
Result.Bottom := I;
|
|
|
|
if (Result.Left >= 0) and (Result.Right < 0) then
|
|
Result.Right := Length(FInfoData.Bricks) - 1;
|
|
|
|
if (Result.Top >= 0) and (Result.Bottom < 0) then
|
|
Result.Bottom := Length(FInfoData.Bricks[0]) - 1;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetCellTextAlignment(
|
|
const ACell: TcxSSCellRec): TcxHorzTextAlign;
|
|
var
|
|
S: string;
|
|
C: Word;
|
|
begin
|
|
with ACell do
|
|
begin
|
|
Result := ACell.StylePtr^.HorzAlign;
|
|
if Result = haGeneral then
|
|
begin
|
|
if DataType = dtFunction then
|
|
S := FormulasCache.FuncRecToDisplayText(ACell.FuncRecPtr, C, False)
|
|
else
|
|
S := Text;
|
|
Result := haLeft;
|
|
if StylePtr^.FormatIndex <> $31 then
|
|
begin
|
|
if cxTryStrToFloat(S) then
|
|
Result := haRight
|
|
else
|
|
if cxTryStrToBool(S) then
|
|
Result := haCenter
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.InvalidateBrick(AColPos, ARowPos: Integer);
|
|
var
|
|
ARect, ARect1: TRect;
|
|
begin
|
|
if (AColPos < 0) or (ARowPos < 0) then Exit;
|
|
ARect := FInfoData.Bricks[AColPos, ARowPos].DisplayRect;
|
|
ARect1 := FInfoData.Bricks[AColPos, ARowPos].TextRect;
|
|
UnionRect(ARect, ARect1, ARect);
|
|
InflateRect(ARect, 3, 3);
|
|
InvalidateRect(ARect);
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.InvalidateCells;
|
|
var
|
|
ARect: TRect;
|
|
begin
|
|
ARect.TopLeft := FInfoData.Bricks[0, 0].DisplayRect.BottomRight;
|
|
ARect.BottomRight := ClipRect.BottomRight;
|
|
InflateRect(ARect, 3, 3);
|
|
InvalidateRect(ARect);
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.SetBrickInfo(AColPos, ARowPos: Integer);
|
|
var
|
|
AColor: Word;
|
|
ACell: TcxSSCellRec;
|
|
AStyle: PcxSSCellStyleRec;
|
|
ABorderState: TcxSSEdgeBorders;
|
|
AHorzAlign: TcxHorzTextAlign;
|
|
AText: TcxString;
|
|
AWidth: Integer;
|
|
ASide: TcxSSEdgeBorder;
|
|
|
|
procedure SetEdgeVertex(const ARect: TRect; var AStyle: TcxSSEdgeLineStyle;
|
|
var AVertex: TcxEdgeVertex; AOrient: TcxOrientation);
|
|
var
|
|
I: Byte;
|
|
const
|
|
AOfs: array[TcxOrientation, 0..1] of Byte = ((0, 1), (1, 0));
|
|
begin
|
|
if (AStyle = lsDefault) and not FDrawGrid then
|
|
AStyle := lsNone;
|
|
if AStyle = lsNone then Exit;
|
|
with ARect do
|
|
if AOrient = oHorz then
|
|
AVertex[0] := Rect(Left, Top, Right - 1, Top)
|
|
else
|
|
AVertex[0] := Rect(Left, Top, Left, Bottom - 1);
|
|
if cxLineWidth[AStyle] = 3 then
|
|
OffsetRect(AVertex[0], -AOfs[AOrient, 0], -AOfs[AOrient, 1]);
|
|
if AStyle = lsDouble then
|
|
for I := 1 to 2 do
|
|
begin
|
|
AVertex[I] := AVertex[0];
|
|
OffsetRect(AVertex[I], AOfs[AOrient, 0] * I, AOfs[AOrient, 1] * I);
|
|
end
|
|
else
|
|
begin
|
|
if AOrient = oHorz then
|
|
Inc(AVertex[0].Bottom, cxLineWidth[AStyle] - 1)
|
|
else
|
|
Inc(AVertex[0].Right, cxLineWidth[AStyle] - 1);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
with FInfoData.Bricks[AColPos, ARowPos] do
|
|
begin
|
|
Col := FInfoData.Bricks[AColPos, 0].Col;
|
|
Row := FInfoData.Bricks[0, ARowPos].Row;
|
|
ACell := DataStorage[Col, Row];
|
|
AStyle := ACell.StylePtr;
|
|
with FInfoData.Bricks[AColPos, 0].DisplayRect do
|
|
begin
|
|
DisplayRect.Left := Left;
|
|
DisplayRect.Right := Right;
|
|
end;
|
|
with FInfoData.Bricks[0, ARowPos].DisplayRect do
|
|
begin
|
|
DisplayRect.Top := Top;
|
|
DisplayRect.Bottom := Bottom;
|
|
end;
|
|
BrushStyle := AStyle^.BrushStyle;
|
|
BrushBkColor := AStyle^.BrushBkColor;
|
|
BrushFgColor := AStyle^.BrushFgColor;
|
|
for ASide := eLeft to eBottom do
|
|
begin
|
|
if ASide = eRight then
|
|
Edges[ASide].Style := DataStorage[Col + 1, Row].StylePtr^.Borders[eLeft].Style
|
|
else
|
|
if ASide = eBottom then
|
|
Edges[ASide].Style := DataStorage[Col, Row + 1].StylePtr^.Borders[eTop].Style
|
|
else
|
|
Edges[ASide].Style := AStyle^.Borders[ASide].Style;
|
|
if (Edges[ASide].Style = lsDefault) and (not FDrawGrid) then
|
|
Edges[ASide].Style := lsNone;
|
|
Edges[ASide].Color := AStyle^.Borders[ASide].Color;
|
|
end;
|
|
IsMerge := cMerge in AStyle^.CellState;
|
|
if not IsMerge then
|
|
begin
|
|
with FInfoData.Selection do
|
|
IsSelected := (not ((Col = Left) and (Row = Top))) and (Col >= Left) and
|
|
(Col <= Right) and (Row >= Top) and (Row <= Bottom);
|
|
AColor := 0;
|
|
AText := ConvertCellRecToDisplayText(ACell, AColor);
|
|
TextRect := DisplayRect;
|
|
InflateRect(TextRect, -2, 0);
|
|
OffsetRect(TextRect, 0, 2);
|
|
Dec(TextRect.Bottom, 2);
|
|
if AText <> '' then
|
|
begin
|
|
Canvas.SelectFont(AStyle^.FontPtr^.FontHandle);
|
|
if AColor = 0 then
|
|
TextSettings.FontColor := AStyle^.FontPtr^.FontColor
|
|
else
|
|
TextSettings.FontColor := AColor;
|
|
AHorzAlign := GetCellTextAlignment(ACell);
|
|
if not AStyle^.WordBreak then
|
|
begin
|
|
AWidth := Canvas.TextWidth(AText) - (TextRect.Right - TextRect.Left) + 4;
|
|
if AWidth > 0 then
|
|
begin
|
|
case AHorzAlign of
|
|
haLeft:
|
|
Inc(TextRect.Right, AWidth);
|
|
haRight:
|
|
Dec(TextRect.Left, AWidth);
|
|
haCenter:
|
|
begin
|
|
Dec(TextRect.Left, AWidth shr 1);
|
|
Inc(TextRect.Right, AWidth shr 1);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
Canvas.CalculateTextExtents(AText, TextRect, AHorzAlign,
|
|
AStyle.VertAlign, AStyle.WordBreak, TextSettings);
|
|
end
|
|
else
|
|
SetLength(TextSettings.TextBricks, 0);
|
|
end;
|
|
if IsMerge then
|
|
begin
|
|
ABorderState := GetMergeCellBorders(Col, Row);
|
|
UnionIndex := AddUnionBrick(Col, Row);
|
|
if UnionIndex >= 0 then
|
|
begin
|
|
BrushStyle := FInfoData.UnionBricks[UnionIndex].BrushStyle;
|
|
BrushBkColor := FInfoData.UnionBricks[UnionIndex].BrushBkColor;
|
|
BrushFgColor := FInfoData.UnionBricks[UnionIndex].BrushFgColor;
|
|
IsSelected := FInfoData.UnionBricks[UnionIndex].IsSelected;
|
|
end;
|
|
end
|
|
else
|
|
ABorderState := [eLeft, eTop];
|
|
SetEdgeVertex(DisplayRect, Edges[eTop].Style, TopEdgeVertex, oHorz);
|
|
SetEdgeVertex(DisplayRect, Edges[eLeft].Style, LeftEdgeVertex, oVert);
|
|
end;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetBottomRow: Integer;
|
|
begin
|
|
Result := GetBottomRight.Y;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetBottomRight: TPoint;
|
|
|
|
procedure GetLastVisible(AHeader: TcxSSHeader; ALen: Integer; var AIndex: Integer);
|
|
var
|
|
ACalcLen: Integer;
|
|
begin
|
|
ACalcLen := 0;
|
|
while ACalcLen < ALen do
|
|
begin
|
|
ACalcLen := ACalcLen + AHeader.Size[AIndex];
|
|
if ACalcLen <= ALen then
|
|
Inc(AIndex);
|
|
end;
|
|
Dec(AIndex);
|
|
while not AHeader.Visible[AIndex] do Dec(AIndex);
|
|
end;
|
|
|
|
var
|
|
ARect: TRect;
|
|
ASize: Integer;
|
|
begin
|
|
ARect := ClipRect;
|
|
Result := FInfoData.Bounds.TopLeft;
|
|
ASize := ARect.Right - ARect.Left - RowHeaderWidth;
|
|
GetLastVisible(Headers[htCol], ASize, Result.X);
|
|
ASize := ARect.Bottom - ARect.Top - ColHeaderHeight;
|
|
GetLastVisible(Headers[htRow], ASize, Result.Y);
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetCanvas: TcxCanvasWrapper;
|
|
begin
|
|
Result := TcxBookSubControlAccess(Owner).Canvas;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetClientRect: TRect;
|
|
begin
|
|
with TcxBookAccess(TcxBookSubControlAccess(Owner).Owner) do
|
|
begin
|
|
Result := ClientBounds;
|
|
if (CaptionBar <> nil) and TcxBookSubControlAccess(CaptionBar).Visible then
|
|
Result.Bottom := TcxBookSubControlAccess(CaptionBar).Top;
|
|
end;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetColHeaderHeight: Integer;
|
|
begin
|
|
if TcxSheetAccess(Owner).ShowHeaders then
|
|
Result := TcxBookAccess(TcxSheetAccess(Owner).Owner).ColHeaderHeight
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetDataStorage: TcxSSDataStorage;
|
|
begin
|
|
Result := TcxSheetAccess(Owner).DataStorage;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetFloatDigits: Byte;
|
|
begin
|
|
Result := TcxBookAccess(TcxSheetAccess(Owner).Owner).Precision;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetFormulasCache: TcxSSFormulasCache;
|
|
begin
|
|
Result := TcxBookAccess(TcxSheetAccess(Owner).Owner).FormulasCache;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetHeader(AType: TcxSSHeaderType): TcxSSHeader;
|
|
begin
|
|
Result := DataStorage.Headers[AType];
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetLeftCol: Integer;
|
|
begin
|
|
Result := TopLeft.X;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetMergeCellBorders(ACol, ARow: Integer): TcxSSEdgeBorders;
|
|
var
|
|
ARect: TRect;
|
|
begin
|
|
Result := [];
|
|
if DataStorage.CheckInMergeRange(Point(ACol, ARow), ARect) then
|
|
begin
|
|
if ACol = ARect.Left then Include(Result, eLeft);
|
|
if ARow = ARect.Top then Include(Result, eTop);
|
|
end;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetRowHeaderWidth: Integer;
|
|
begin
|
|
with TcxBookAccess(TcxSheetAccess(Owner).Owner) do
|
|
if ShowHeaders then
|
|
Result := RowHeaderWidth
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetTopLeft: TPoint;
|
|
begin
|
|
Result := FInfoData.Bounds.TopLeft;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetTopRow: Integer;
|
|
begin
|
|
Result := GetTopLeft.Y;
|
|
end;
|
|
|
|
function TcxSSheetViewInfo.GetRightCol: Integer;
|
|
begin
|
|
Result := GetBottomRight.X;
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.SetBottomRight(const Value: TPoint);
|
|
|
|
function CalculateTopLeft: TPoint;
|
|
var
|
|
ARect: TRect;
|
|
ASize: Integer;
|
|
begin
|
|
Result := Value;
|
|
ARect := ClipRect;
|
|
ASize := ARect.Right - ARect.Left - RowHeaderWidth;
|
|
Result.X := Result.X +
|
|
TcxHeaderAccess(DataStorage.Headers[htCol]).GetCountItems(Result.X, ASize, False) + 1;
|
|
ASize := ARect.Bottom - ARect.Top - ColHeaderHeight;
|
|
Result.Y := Result.Y +
|
|
TcxHeaderAccess(DataStorage.Headers[htRow]).GetCountItems(Result.Y, ASize, False) + 1;
|
|
end;
|
|
|
|
begin
|
|
SetTopLeft(CalculateTopLeft);
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.SetBottomRow(const Value: Integer);
|
|
begin
|
|
SetBottomRight(Point(GetBottomRight.X, Value));
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.SetInfoChanged(const Value: Boolean);
|
|
begin
|
|
if FInfoChanged <> Value then
|
|
begin
|
|
FInfoChanged := Value;
|
|
if Value then
|
|
if TcxSheetAccess(Owner).Owner.BufferedPaint then
|
|
TcxSheetAccess(Owner).Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.SetLeftCol(const Value: Integer);
|
|
begin
|
|
SetTopLeft(Point(Value, TopRow));
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.SetRightCol(const Value: Integer);
|
|
begin
|
|
SetBottomRight(Point(Value, GetBottomRight.Y));
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.SetTopLeft(Value: TPoint);
|
|
|
|
function CheckValue: Boolean;
|
|
begin
|
|
with FInfoData.Bounds do
|
|
Result := (Left = Value.X) and (Top = Value.Y);
|
|
if not Result then
|
|
begin
|
|
if Value.Y < 0 then Value.Y := 0;
|
|
if Value.X < 0 then Value.X := 0;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
AListener: TcxListenerAccess;
|
|
|
|
begin
|
|
if CheckValue then Exit;
|
|
AListener := TcxListenerAccess(TcxBookAccess(TcxSheetAccess(Owner).Owner).Listener);
|
|
AListener.OnTopLeftChanging(TcxSSBookSheet(Owner), Value);
|
|
if not CheckValue then
|
|
begin
|
|
FInfoData.Bounds.TopLeft := Value;
|
|
CalculateViewInfo;
|
|
TcxSheetAccess(FOwner).Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSSheetViewInfo.SetTopRow(const Value: Integer);
|
|
begin
|
|
SetTopLeft(Point(LeftCol, Value));
|
|
end;
|
|
|
|
end.
|