아래에 답변한 내용은 if 질문이 Align이면 참고하시고 else Sort이면 이부분을 참조하세요
소스를 올리니까 참조하세요...
{*******************************************************************************
Class: TSortGrid
xapisoft
kwkim@xapisoft.com
*******************************************************************************}
unit SortGrid;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Grids;
type
TSortStyle = (ssAutomatic, ssAlphabetic, ssNumeric, ssDate);
TSortDirection = (sdAscending, sdDescending);
{**************************************************************}
{*** NOTE: These are the options you can set to affect sorting.}
TSortOptions = record
SortStyle: TSortStyle;
SortDirection: TSortDirection;
SortCaseSensitive: Boolean;
end;
{ Don't pay any attention to these unless you want to
understand the internals of how I sort the grid. }
TSortedListEntry = record
Str: String;
RowNum: Longint;
end;
PSortedListEntry = ^TSortedListEntry;
TSortedList = class(TList)
public
function GetItem(const i: Integer): PSortedListEntry;
procedure Reset;
end;
{ Resume paying attention. }
TCellBevelStyle = (cbNone, cbRaised, cbLowered);
{**********************************************************}
{*** NOTE: This is one of the structures in TFormatOptions.}
TCellBevel = record
Style: TCellBevelStyle;
UpperLeftColor: TColor;
LowerRightColor: TColor;
end;
TVertAlignment = (taTopJustify, taBottomJustify, taMiddle);
{**************************************************}
{*** NOTE: These are the display options you can set
for each cell in OnGetCellFormat.}
TFormatOptions = record
Brush: TBrush;
Font: TFont;
AlignmentHorz: TAlignment;
AlignmentVert: TVertAlignment;
Bevel: TCellBevel;
HideText: Boolean;
end;
{These are the new events defined for TSortGrid.}
TFormatDrawCellEvent = procedure(Sender: TObject; Col, Row: Longint;
State: TGridDrawState;
var FormatOptions: TFormatOptions) of object;
TClickSortEvent = procedure(Sender: TObject; Col, Row: Longint;
var SortOptions: TSortOptions) of object;
TUpdateGridEvent = procedure(Sender: TObject; Index: Longint) of object;
TSizeChangedEvent = procedure(Sender: TObject; OldColCount, OldRowCount: Longint) of object;
TBeginSortEvent = procedure(Sender: TObject; Col: Longint; var SortOptions: TSortOptions) of object;
TEndSortEvent = procedure(Sender: TObject; Col: Longint) of object;
TCellValidateEvent = procedure(Sender: TObject; Col, Row: Longint;
var Value: String; var Valid: Boolean) of object;
{Here's the main new class: TSortGrid}
TSortGrid = class(TStringGrid)
private
{ Private declarations }
fSortedList: TSortedList;
fAlignmentHorz: TAlignment;
fAlignmentVert: TVertAlignment;
fClickSorting: Boolean;
fBevelStyle: TCellBevelStyle;
fProportionalScrollBars: Boolean;
fExtendedKeys: Boolean;
fSorting: Boolean;
fModified: Boolean;
fOldCellText: String;
fOldCol, fOldRow: Longint;
fOldModifiedValue: Boolean;
fEntered: Boolean;
fOnGetCellFormat: TFormatDrawCellEvent;
fOnClickSort: TClickSortEvent;
fOnRowInsert: TUpdateGridEvent;
fOnRowDelete: TUpdateGridEvent;
fOnColumnInsert: TUpdateGridEvent;
fOnColumnDelete: TUpdateGridEvent;
fOnColumnWidthsChanged: TNotifyEvent;
fOnRowHeightsChanged: TNotifyEvent;
fOnSizeChanged: TSizeChangedEvent;
fOnBeginSort: TBeginSortEvent;
fOnEndSort: TEndSortEvent;
fOnCellValidate: TCellValidateEvent;
procedure SetAlignmentHorz(Value: TAlignment);
procedure SetAlignmentVert(Value: TVertAlignment);
procedure SetBevelStyle(Value: TCellBevelStyle);
procedure WMSize(var Msg: TWMSize); message WM_SIZE;
procedure SetProportionalScrollBars(Value: Boolean);
protected
{ Protected declarations }
procedure ListQuickSort(const ACol: Longint; const SortOptions: TSortOptions); virtual;
function DetermineSortStyle(const ACol: Longint): TSortStyle; virtual;
procedure InitializeFormatOptions(var FmtOpts: TFormatOptions);
procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure ColWidthsChanged; override;
procedure RowHeightsChanged; override;
procedure SizeChanged(OldColCount, OldRowCount: Longint); override;
procedure UpdateScrollPage; virtual;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure SetEditText(ACol, ARow: Longint; const Value: string); override;
procedure Click; override;
procedure DoEnter; override;
procedure DoExit; override;
procedure InitValidate; virtual;
public
{ Public declarations }
property Sorting: Boolean read fSorting default False;
property Modified: Boolean read fModified write fModified default False;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure MoveTo(ACol, ARow: Longint); virtual;
procedure Clear; virtual;
procedure InsertRow(ARow: Longint); virtual;
procedure InsertColumn(ACol: Longint); virtual;
procedure DeleteRow(ARow: Longint); virtual;
procedure DeleteColumn(ACol: Longint); virtual;
procedure MoveRow(FromIndex, ToIndex: Longint); virtual;
procedure MoveColumn(FromIndex, ToIndex: Longint); virtual;
procedure SwapRows(ARow1, ARow2: Longint); virtual;
procedure SwapColumns(ACol1, ACol2: Longint); virtual;
procedure AutoSizeCol(const ACol: LongInt); virtual;
procedure AutoSizeColumns(const DoFixedCols: Boolean; const Padding: Integer); virtual;
procedure SortByColumn(const ACol: LongInt; SortOptions: TSortOptions); virtual;
function IsCell(const Value: String; var ACol, ARow: Longint): Boolean; virtual;
procedure LoadFromFile(const FileName: String; const Delimiter: Char); virtual;
procedure SaveToFile(const FileName: String; const Delimiter: Char); virtual;
function CanUndoSort: Boolean; virtual;
procedure UndoSort; virtual;
function GetCellDrawState(const ACol, ARow: Longint): TGridDrawState;
function SelectCell(ACol, ARow: Longint): Boolean; override;
procedure ValidateCell; virtual;
published
{ Published declarations }
property AlignmentHorz: TAlignment read fAlignmentHorz write SetAlignmentHorz default taLeftJustify;
property AlignmentVert: TVertAlignment read fAlignmentVert write SetAlignmentVert default taTopJustify;
property ClickSorting: Boolean read fClickSorting write fClickSorting default True;
property BevelStyle: TCellBevelStyle read fBevelStyle write SetBevelStyle default cbNone;
property ProportionalScrollBars: Boolean read fProportionalScrollBars write SetProportionalScrollBars default True;
property ExtendedKeys: Boolean read fExtendedKeys write fExtendedKeys default False;
{ Published events }
property OnGetCellFormat: TFormatDrawCellEvent read fOnGetCellFormat write fOnGetCellFormat;
property OnClickSort: TClickSortEvent read fOnClickSort write fOnClickSort;
property OnRowInsert: TUpdateGridEvent read fOnRowInsert write fOnRowInsert;
property OnRowDelete: TUpdateGridEvent read fOnRowDelete write fOnRowDelete;
property OnColumnInsert: TUpdateGridEvent read fOnColumnInsert write fOnColumnInsert;
property OnColumnDelete: TUpdateGridEvent read fOnColumnDelete write fOnColumnDelete;
property OnColumnWidthsChanged: TNotifyEvent read fOnColumnWidthsChanged write fOnColumnWidthsChanged;
property OnRowHeightsChanged: TNotifyEvent read fOnRowHeightsChanged write fOnRowHeightsChanged;
property OnSizeChanged: TSizeChangedEvent read fOnSizeChanged write fOnSizeChanged;
property OnBeginSort: TBeginSortEvent read fOnBeginSort write fOnBeginSort;
property OnEndSort: TEndSortEvent read fOnEndSort write fOnEndSort;
property OnCellValidate: TCellValidateEvent read fOnCellValidate write fOnCellValidate;
end;
procedure Register;
implementation
{$R SortGrid.Res}
var
//This is here for Compare. I can't pass it as a parameter,
//and I can't make Compare a method. So I had to use a global. :-(
GlobalSortOptions: TSortOptions;
{******************************************************************************}
{** Miscellaneous Non-Member Functions **}
{******************************************************************************}
procedure TokenizeGridString(const S: String; const Delimiter: Char; Tokens: TStringList);
var
i, Len: Integer;
CurToken: String;
begin
Tokens.Clear;
CurToken:='';
Len:=Length(S);
for i:=1 to Len do
begin
if S[i] = Delimiter then
begin
Tokens.Add(CurToken);
CurToken:='';
end
else
CurToken:=CurToken+S[i];
end;
Tokens.Add(CurToken);
end;
function StringCompare(const Str1, Str2: String): Integer;
begin
if Str1 < Str2 then Result:=-1
else if Str2 < Str1 then Result:=1
else Result:=0;
end;
function DateCompare(const Str1, Str2: String): Integer;
var
Val1, Val2: TDateTime;
begin
try
Val1:=StrToDateTime(Str1);
Val2:=StrToDateTime(Str2);
if Val1 < Val2 then Result:=-1
else if Val2 < Val1 then Result:=1
else Result:=0;
except
on EConvertError do Result:=0;
end;
end;
function NumericCompare(const Str1, Str2: String): Integer;
var
Val1, Val2: Extended;
begin
try
Val1:=StrToFloat(Str1);
Val2:=StrToFloat(Str2);
if Val1 < Val2 then Result:=-1
else if Val2 < Val1 then Result:=1
else Result:=0;
except
on EConvertError do Result:=0;
end;
end;
//This looks at the global variable GlobalSortOptions.
//I hated to use a global, but I can't pass any additional
//parameters to Compare, and I can't make Compare a
//method of an object. A global seemed the only choice.
function Compare(Item1, Item2: Pointer): Integer;
var
Entry1, Entry2: PSortedListEntry;
begin
Entry1:=Item1;
Entry2:=Item2;
//Handle Case-Insensitivity.
if not GlobalSortOptions.SortCaseSensitive then
begin
Entry1^.Str:=Uppercase(Entry1^.Str);
Entry2^.Str:=Uppercase(Entry2^.Str);
end;
//Determine compare type and do the comparison.
case GlobalSortOptions.SortStyle of
ssNumeric: Result:=NumericCompare(Entry1^.Str, Entry2^.Str);
ssDate: Result:=DateCompare(Entry1^.Str, Entry2^.Str);
else
Result:=StringCompare(Entry1^.Str, Entry2^.Str);
end;
//Now, make sure we don't swap the rows if the Keys are equal.
//If they're equal then we sort by row number.
if Result = 0 then
begin
if Entry1^.RowNum < Entry2^.RowNum then Result:=-1
else if Entry1^.RowNum > Entry2^.RowNum then Result:=1
else Result:=0; //Sometimes an item does get compared to itself.
end
else //Reverse polarity if descending sort.
if GlobalSortOptions.SortDirection = sdDescending then
Result:=-1*Result;
end;
//I put TSortGrid on the 'Additional' page because
//I like having it with the other grids.
procedure Register;
begin
RegisterComponents('Additional', [TSortGrid]);
end;
{ ----------------------============******============---------------------- }
{******************************************************************************}
{** Public Members for TSortedList **}
{******************************************************************************}
function TSortedList.GetItem(const i: Integer): PSortedListEntry;
begin
//Cast the pointer.
Result:=PSortedListEntry(Items[i]);
end;
procedure TSortedList.Reset;
var
i: Integer;
begin
//Dispose of anything in the list first.
for i:=0 to Count-1 do
if Items[i] <> nil then
Dispose(Items[i]);
//Now clear the list.
Clear;
end;
{ ----------------------============******============---------------------- }
{******************************************************************************}
{** Private Members for TSortGrid **}
{******************************************************************************}
procedure TSortGrid.SetAlignmentHorz(Value: TAlignment);
begin
fAlignmentHorz:=Value;
Invalidate;
end;
procedure TSortGrid.SetAlignmentVert(Value: TVertAlignment);
begin
fAlignmentVert:=Value;
Invalidate;
end;
procedure TSortGrid.SetBevelStyle(Value: TCellBevelStyle);
begin
fBevelStyle:=Value;
Invalidate;
end;
procedure TSortGrid.WMSize(var Msg: TWMSize);
begin
inherited;
UpdateScrollPage;
end;
procedure TSortGrid.SetProportionalScrollBars(Value: Boolean);
begin
fProportionalScrollBars:=Value;
UpdateScrollPage;
end;
{******************************************************************************}
{** Protected Members for TSortGrid **}
{******************************************************************************}
procedure TSortGrid.ListQuickSort(const ACol: Longint; const SortOptions: TSortOptions);
var
i: Integer;
Item: PSortedListEntry;
BufferGrid: TStringGrid;
begin
//Let everyone know we're sorting.
fSorting:=True;
try
//Get rid of any old entries in the sorted list.
fSortedList.Reset;
//Set the sort options for the list.
//"Compare" can only look at GlobalSortOptions.
GlobalSortOptions:=SortOptions;
//Insert the Row Number and Key (Str) into
for i:=FixedRows to RowCount-1 do
begin
New(Item);
Item^.RowNum:=i;
Item^.Str:=Cells[ACol, i];
fSortedList.Add(Item);
end;
//Quick Sort the list by key string.
//Then the row numbers will indicate where
//each row should be placed.
//E.g. If list item 0 contains a RowNum of 4 then
//row 4 should be the first row (position 0).
fSortedList.Sort(Compare);
//Now rearrange the rows of the grid in sorted order.
//This is a fast but space inefficient way to do it.
//First, create a buffer grid and size it correctly.
BufferGrid:=TStringGrid.Create(Self);
try
BufferGrid.ColCount:=ColCount;
BufferGrid.RowCount:=RowCount;
//Copy the rows to the buffer grid in sorted order.
for i:=0 to fSortedList.Count-1 do
begin
Item:=fSortedList.GetItem(i);
BufferGrid.Rows[i+FixedRows].Assign(Rows[Item^.RowNum]);
end;
//Now put the rows back into the original grid.
for i:=FixedRows to RowCount-1 do
begin
Rows[i].Assign(BufferGrid.Rows[i]);
end;
finally
BufferGrid.Free;
end;
//Now put the selection back on the right row.
for i:=0 to fSortedList.Count-1 do
begin
Item:=fSortedList.GetItem(i);
if Item^.RowNum = Row then
begin
MoveTo(Col, i+FixedRows);
Break;
end;
end;
finally
//Make sure we get this turned off.
fSorting:=False;
end;
end;
//This function tries to determine the best sort style
//for a column. If all the entries can be converted to
//numbers, a numeric sort is returned. If they can all
//be converted to dates, a date sort is returned.
//Otherwise, an alphabetic sort is returned.
function TSortGrid.DetermineSortStyle(const ACol: Longint): TSortStyle;
var
i: Integer;
DoNumeric, DoDate: Boolean;
begin
DoNumeric:=True;
DoDate:=True;
//Note: We only go through the rows once.
//This code depends on the fact that no
//entry can be both a date and number.
//If that fails to hold true, this will
//return a numeric sort.
for i:=FixedRows to RowCount-1 do
begin
if DoNumeric then
begin
try
StrToFloat(Cells[ACol, i]);
except
on EConvertError do
begin
DoNumeric:=False;
if not DoDate then Break;
end;
end;
end;
if DoDate then
begin
try
StrToDate(Cells[ACol, i]);
except
on EConvertError do
begin
DoDate:=False;
if not DoNumeric then Break;
end;
end;
end;
end;
if DoNumeric then Result := ssNumeric
else if DoDate then Result := ssDate
else Result := ssAlphabetic;
end;
procedure TSortGrid.InitializeFormatOptions(var FmtOpts: TFormatOptions);
begin
//Setup good defaults for FormatOptions.
FmtOpts.HideText:=False;
FmtOpts.Font:=Canvas.Font;
FmtOpts.Brush:=Canvas.Brush;
FmtOpts.AlignmentHorz:=AlignmentHorz;
FmtOpts.AlignmentVert:=AlignmentVert;
FmtOpts.Bevel.Style:=BevelStyle;
//Set defaults for the bevel colors.
case BevelStyle of
cbRaised:
begin
FmtOpts.Bevel.UpperLeftColor:=clBtnHighlight;
FmtOpts.Bevel.LowerRightColor:=clBtnShadow;
end;
cbLowered:
begin
FmtOpts.Bevel.UpperLeftColor:=clBtnShadow;
FmtOpts.Bevel.LowerRightColor:=clBtnHighlight;
end;
else
FmtOpts.Bevel.UpperLeftColor:=clWindow;
FmtOpts.Bevel.LowerRightColor:=clWindow;
end;
end;
procedure TSortGrid.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
var
xOffset, yOffset: Integer;
FmtOpts: TFormatOptions;
begin
InitializeFormatOptions(FmtOpts);
//Now do the OnGetCellFormat event if necessary.
if Assigned(fOnGetCellFormat) then
fOnGetCellFormat(Self, ACol, ARow, AState, FmtOpts);
if DefaultDrawing then
begin
//Calculate horizontal offset.
case FmtOpts.AlignmentHorz of
taRightJustify:
xOffset:=ARect.Right-ARect.Left-Canvas.TextWidth(Cells[ACol, ARow])-2;
taCenter:
xOffset:=(ARect.Right-ARect.Left-Canvas.TextWidth(Cells[ACol, ARow])) div 2;
else
xOffset:=2;
end;
//Calculate vertical offset.
case FmtOpts.AlignmentVert of
taBottomJustify:
yOffset:=ARect.Bottom-ARect.Top-Canvas.TextHeight(Cells[ACol, ARow])-3;
taMiddle:
yOffset:=(ARect.Bottom-ARect.Top-Canvas.TextHeight(Cells[ACol, ARow])) div 2;
else
yOffset:=2;
end;
//Now do the text drawing.
if not FmtOpts.HideText then
Canvas.TextRect(ARect, ARect.Left+xOffset, ARect.Top+yOffset, Cells[ACol, ARow])
else
Canvas.TextRect(ARect, ARect.Left+xOffset, ARect.Top+yOffset, '');
//Draw Bevel.
if (FmtOpts.Bevel.Style <> cbNone) and
(ACol >= FixedCols) and (ARow >= FixedRows) then
begin
//Draw from bottom-most lines out to mimic behaviour of
//fixed cells when FixedXXXXLine is toggled.
with ARect do
begin
if goFixedVertLine in Options then
begin
Canvas.Pen.Color := FmtOpts.Bevel.LowerRightColor;
Canvas.PolyLine([Point(Right-1, Top), Point(Right-1, Bottom)]);
end;
if goFixedHorzLine in Options then
begin
Canvas.Pen.Color := FmtOpts.Bevel.LowerRightColor;
Canvas.PolyLine([Point(Left, Bottom-1), Point(Right, Bottom-1)]);
end;
if goFixedVertLine in Options then
begin
Canvas.Pen.Color := FmtOpts.Bevel.UpperLeftColor;
Canvas.PolyLine([Point(Left, Top), Point(Left, Bottom)]);
end;
if goFixedHorzLine in Options then
begin
Canvas.Pen.Color := FmtOpts.Bevel.UpperLeftColor;
Canvas.PolyLine([Point(Left, Top), Point(Right, Top)]);
end;
end;
end;
end
else
inherited DrawCell(ACol, ARow, ARect, AState);
end;
procedure TSortGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
Point: TPoint;
ACol, ARow: LongInt;
SortOptions: TSortOptions;
CurrentCursor: TCursor;
begin
//Make sure we're not sizing and have a header row.
if Focused and
ClickSorting and
(fGridState = gsNormal) and
(FixedRows >= 1) and
(Shift = []) then
begin
Point.x:=X;
Point.y:=Y;
MouseToCell(Point.x, Point.y, ACol, ARow);
//Make sure they clicked a fixed row.
if (ARow >= 0) and (ARow <= (FixedRows-1)) then
begin
SortOptions.SortStyle:=ssAutomatic;
if Button = mbRight then SortOptions.SortDirection:=sdDescending
else SortOptions.SortDirection:=sdAscending;
SortOptions.SortCaseSensitive:=True;
if Assigned(fOnClickSort) then
fOnClickSort(Self, ACol, ARow, SortOptions);
CurrentCursor:=Screen.Cursor;
try
Screen.Cursor:=crHourglass;
SortByColumn(ACol, SortOptions);
finally
Screen.Cursor:=CurrentCursor;
end;
end;
end;
inherited MouseUp(Button, Shift, X, Y);
end;
procedure TSortGrid.ColWidthsChanged;
begin
inherited ColWidthsChanged;
if Assigned(fOnColumnWidthsChanged) then fOnColumnWidthsChanged(Self);
UpdateScrollPage;
end;
procedure TSortGrid.RowHeightsChanged;
begin
inherited RowHeightsChanged;
if Assigned(fOnRowHeightsChanged) then fOnRowHeightsChanged(Self);
UpdateScrollPage;
end;
procedure TSortGrid.SizeChanged(OldColCount, OldRowCount: Longint);
begin
inherited SizeChanged(OldColCount, OldRowCount);
if Assigned(fOnSizeChanged) then fOnSizeChanged(Self, OldColCount, OldRowCount);
UpdateScrollPage;
end;
procedure TSortGrid.UpdateScrollPage;
function LMax(const A, B: Longint): Longint;
begin
Result:=B;
if A > B then Result:=A;
end;
var
SI: TScrollInfo;
begin
{Make the scroll bar(s) proportional.}
if (ScrollBars = ssVertical) or (ScrollBars = ssBoth) then
begin
SI.cbSize:=sizeof(SI);
SI.fMask:=SIF_PAGE or SIF_POS or SIF_RANGE;
GetScrollInfo(Handle, SB_VERT, SI);
SI.fMask:=SIF_PAGE;
if ProportionalScrollBars then
begin
SI.nPage:=LMax(Round(((1.0*(VisibleRowCount+FixedRows))/RowCount)*(SI.nMax-SI.nMin+1)), 1)
end
else
SI.nPage:=0;
SetScrollInfo(Handle, SB_VERT, SI, True);
end;
if (ScrollBars = ssHorizontal) or (ScrollBars = ssBoth) then
begin
SI.cbSize:=sizeof(SI);
SI.fMask:=SIF_PAGE or SIF_POS or SIF_RANGE;
GetScrollInfo(Handle, SB_HORZ, SI);
SI.fMask:=SIF_PAGE;
if ProportionalScrollBars then
begin
SI.nPage:=LMax(Round(((1.0*(VisibleColCount+FixedCols))/ColCount)*(SI.nMax-SI.nMin+1)), 1)
end
else
SI.nPage:=0;
SetScrollInfo(Handle, SB_HORZ, SI, True);
end;
end;
procedure TSortGrid.KeyDown(var Key: Word; Shift: TShiftState);
begin
inherited KeyDown(Key, Shift);
if ExtendedKeys and not EditorMode then
begin
if Shift = [ssCtrl] then
begin
case Key of
VK_INSERT: InsertRow(Row);
VK_DELETE: if RowCount > (FixedRows+1) then DeleteRow(Row);
end;
end
else if Shift = [ssCtrl, ssShift] then
begin
case Key of
VK_INSERT: InsertColumn(Col);
VK_DELETE: if ColCount > (FixedCols+1) then DeleteColumn(Col);
end;
end;
end;
end;
procedure TSortGrid.SetEditText(ACol, ARow: Longint; const Value: string);
begin
try
if Value <> Cells[ACol, ARow] then Modified:=True;
finally
inherited SetEditText(ACol, ARow, Value);
end;
end;
procedure TSortGrid.Click;
begin
try
inherited Click;
finally
if fEntered then ValidateCell;
end;
end;
procedure TSortGrid.DoEnter;
begin
try
inherited DoEnter;
fEntered:=True;
finally
InitValidate;
end;
end;
procedure TSortGrid.DoExit;
begin
try
Click;
finally
inherited DoExit;
fEntered:=False;
end;
end;
procedure TSortGrid.InitValidate;
begin
fOldCol:=Col;
fOldRow:=Row;
fOldCellText:=Cells[fOldCol, fOldRow];
fOldModifiedValue:=Modified;
end;
{******************************************************************************}
{** Public Members for TSortGrid **}
{******************************************************************************}
constructor TSortGrid.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fSortedList:=TSortedList.Create;
fAlignmentHorz:=taLeftJustify;
fAlignmentVert:=taTopJustify;
fClickSorting:=True;
fBevelStyle:=cbNone;
fProportionalScrollBars:=True;
fExtendedKeys:=False;
fSorting:=False;
fModified:=False;
fEntered:=False;
InitValidate;
end;
destructor TSortGrid.Destroy;
begin
fSortedList.Reset;
fSortedList.Free;
inherited Destroy;
end;
procedure TSortGrid.ValidateCell;
var
Value: String;
Valid: Boolean;
begin
if fOldCellText<>Cells[fOldCol, fOldRow] then
begin
Value:=Cells[fOldCol, fOldRow];
Valid:=True;
if Assigned(fOnCellValidate) then
fOnCellValidate(Self, fOldCol, fOldRow, Value, Valid);
//Since Value is also a VAR parameter, we always
//use it if it was changed in OnCellValidate.
if not Valid then
begin
if Value <> Cells[fOldCol, fOldRow] then
Cells[fOldCol, fOldRow]:=Value
else
Cells[fOldCol, fOldRow]:=fOldCellText;
Modified:=fOldModifiedValue;
end
else if Value <> Cells[fOldCol, fOldRow] then
Cells[fOldCol, fOldRow]:=Value;
end;
InitValidate;
end;
//AutoSizes the ACol column.
procedure TSortGrid.AutoSizeCol(const ACol: LongInt);
var
MaxWidth, TextW, i: Integer;
FmtOpts: TFormatOptions;
begin
//Resize the column to display the largest value.
MaxWidth:=0;
Canvas.Font:=Font;
for i:=0 to RowCount-1 do
begin
InitializeFormatOptions(FmtOpts);
if Assigned(fOnGetCellFormat) then
fOnGetCellFormat(Self, Col, i, GetCellDrawState(ACol, i), FmtOpts);
Canvas.Font:=FmtOpts.Font;
TextW:=Canvas.TextWidth(Cells[ACol, i]);
if TextW > MaxWidth then MaxWidth:=TextW;
end;
ColWidths[ACol]:=MaxWidth+Canvas.TextWidth('x');
end;
//AutoSizes ALL the variable columns and optionally the fixed columns.
procedure TSortGrid.AutoSizeColumns(const DoFixedCols: Boolean; const Padding: Integer);
var
i: Integer;
begin
if DoFixedCols then
for i:=0 to FixedCols-1 do
begin
AutoSizeCol(i);
if Padding <> 0 then
ColWidths[i]:=ColWidths[i]+Padding;
end;
for i:=FixedCols to ColCount-1 do
begin
AutoSizeCol(i);
if Padding <> 0 then
ColWidths[i]:=ColWidths[i]+Padding;
end;
end;
//Sorts the variable rows using Column ACol
//as a key and with the options you specify.
procedure TSortGrid.SortByColumn(const ACol: LongInt; SortOptions: TSortOptions);
begin
//Don't sort while in edit mode.
if not EditorMode then
begin
//If there's only one row we don't need to do anything.
if RowCount > (FixedRows+1) then
begin
//Call the OnBeginSort event.
if Assigned(fOnBeginSort) then fOnBeginSort(Self, ACol, SortOptions);
//Now we do the Automatic sorting determination.
if SortOptions.SortStyle = ssAutomatic then
SortOptions.SortStyle:=DetermineSortStyle(ACol);
//Quick Sort column ACol.
ListQuickSort(ACol, SortOptions);
//Call the OnEndSort event.
if Assigned(fOnEndSort) then fOnEndSort(Self, ACol);
end;
end;
end;
procedure TSortGrid.InsertRow(ARow: Longint);
begin
RowCount:=RowCount+1;
MoveRow(RowCount-1, ARow);
Rows[ARow].Clear;
Row:=ARow;
if Assigned(fOnRowInsert) then fOnRowInsert(Self, ARow);
end;
procedure TSortGrid.InsertColumn(ACol: Longint);
begin
ColCount:=ColCount+1;
MoveColumn(ColCount-1, ACol);
Cols[ACol].Clear;
Col:=ACol;
if Assigned(fOnColumnInsert) then fOnColumnInsert(Self, ACol);
end;
procedure TSortGrid.DeleteRow(ARow: Longint);
var
ASE: Boolean;
begin
Rows[ARow].Clear;
{If goAlwaysShowEditor is enabled then DeleteRow
and MoveRow leave the caret past the last row or
in one of the fixed rows. So I turn it off before
the delete and then back on after to get it
working correctly.}
ASE:=False;
if goAlwaysShowEditor in Options then
begin
Options:=Options-[goAlwaysShowEditor];
ASE:=True;
end;
inherited DeleteRow(ARow);
if ASE then Options:=Options+[goAlwaysShowEditor];
if Assigned(fOnRowDelete) then fOnRowDelete(Self, ARow);
end;
procedure TSortGrid.DeleteColumn(ACol: Longint);
var
ASE: Boolean;
begin
Cols[ACol].Clear;
//See DeleteRow for comments...
ASE:=False;
if goAlwaysShowEditor in Options then
begin
Options:=Options-[goAlwaysShowEditor];
ASE:=True;
end;
inherited DeleteColumn(ACol);
if ASE then Options:=Options+[goAlwaysShowEditor];
if Assigned(fOnColumnDelete) then fOnColumnDelete(Self, ACol);
end;
procedure TSortGrid.MoveRow(FromIndex, ToIndex: Longint);
var
ASE: Boolean;
begin
//See DeleteRow for comments...
ASE:=False;
if goAlwaysShowEditor in Options then
begin
Options:=Options-[goAlwaysShowEditor];
ASE:=True;
end;
inherited MoveRow(FromIndex, ToIndex);
if ASE then Options:=Options+[goAlwaysShowEditor];
end;
procedure TSortGrid.MoveColumn(FromIndex, ToIndex: Longint);
var
ASE: Boolean;
begin
//See DeleteRow for comments...
ASE:=False;
if goAlwaysShowEditor in Options then
begin
Options:=Options-[goAlwaysShowEditor];
ASE:=True;
end;
inherited MoveColumn(FromIndex, ToIndex);
if ASE then Options:=Options+[goAlwaysShowEditor];
end;
//The logic gets around a weird case where you swap with the last row.
procedure TSortGrid.SwapRows(ARow1, ARow2: Longint);
begin
if ARow1 < ARow2 then
begin
MoveRow(ARow2, ARow1);
MoveRow(ARow1+1, ARow2);
end
else if ARow2 < ARow1 then
begin
MoveRow(ARow1, ARow2);
MoveRow(ARow2+1, ARow1);
end;
end;
//The logic gets around a weird case where you swap with the last column.
procedure TSortGrid.SwapColumns(ACol1, ACol2: Longint);
begin
if ACol1 < ACol2 then
begin
MoveColumn(ACol2, ACol1);
MoveColumn(ACol1+1, ACol2);
end
else if ACol2 < ACol1 then
begin
MoveColumn(ACol1, ACol2);
MoveColumn(ACol2+1, ACol1);
end;
end;
//Moves the selected cell to (ACol, ARow) and makes it visible.
procedure TSortGrid.MoveTo(ACol, ARow: Longint);
begin
if ACol < FixedCols then ACol:=FixedCols;
if ARow < FixedRows then ARow:=FixedRows;
if SelectCell(ACol, ARow) then
begin
Col:=ACol;
Row:=ARow;
MoveColRow(ACol, ARow, True, True);
end;
end;
//Clears the grid.
procedure TSortGrid.Clear;
var
i: Longint;
begin
for i:= 0 to (ColCount-1) do
begin
Cols[i].Clear;
end;
end;
//Finds a string in the grid.
//It searches by column and returns the first instance it finds.
function TSortGrid.IsCell(const Value: String; var ACol, ARow: Longint): Boolean;
var
i, Place: Longint;
begin
Result := False;
for i := 0 to ColCount-1 do
begin
Place:=Cols[i].IndexOf(Value);
if Place >= 0 then
begin
ARow := Place;
ACol := i;
Result := True;
break;
end;
end;
end;
procedure TSortGrid.LoadFromFile(const FileName: String; const Delimiter: Char);
var
r: Longint;
Lines, Fields: TStringList;
begin
Lines:=TStringList.Create;
Fields:=TStringList.Create;
try
Clear;
Lines.LoadFromFile(FileName);
RowCount:=Lines.Count;
ColCount:=FixedCols+1;
for r:=0 to Lines.Count-1 do
begin
TokenizeGridString(Lines[r], Delimiter, Fields);
if Fields.Count > ColCount then ColCount:=Fields.Count;
Rows[r].Assign(Fields);
end;
finally
Fields.Free;
Lines.Free;
end;
end;
procedure TSortGrid.SaveToFile(const FileName: String; const Delimiter: Char);
var
r, c: Longint;
BufStr: String;
Lines: TStringList;
begin
Lines:=TStringList.Create;
try
Lines.Clear;
for r:=0 to RowCount-1 do
begin
BufStr:='';
for c:=0 to ColCount-1 do
begin
BufStr:=BufStr+Cells[c, r];
if c<>(ColCount-1) then BufStr:=BufStr+Delimiter;
end;
Lines.Add(BufStr);
end;
Lines.SaveToFile(FileName);
finally
Lines.Free;
end;
end;
function TSortGrid.CanUndoSort: Boolean;
begin
//We can only undo a sort if we still have exactly the
//same number of rows that we did when we sorted.
Result:=(fSortedList.Count = (RowCount-FixedRows));
if Result = False then fSortedList.Reset;
end;
procedure TSortGrid.UndoSort;
var
BufferGrid: TStringGrid;
Item: PSortedListEntry;
i: Integer;
begin
if CanUndoSort then
begin
BufferGrid:=TStringGrid.Create(Self);
try
BufferGrid.ColCount:=ColCount;
BufferGrid.RowCount:=RowCount;
//Copy the rows to the buffer grid in the current order.
for i:=FixedRows to RowCount-1 do
begin
BufferGrid.Rows[i].Assign(Rows[i]);
end;
//Now put the rows back into the original grid in the old order.
for i:=0 to fSortedList.Count-1 do
begin
Item:=fSortedList.GetItem(i);
Rows[Item^.RowNum].Assign(BufferGrid.Rows[i+FixedRows]);
end;
finally
BufferGrid.Free;
end;
//Now put the selection back on the right row.
Item:=fSortedList.GetItem(Row-FixedRows);
MoveTo(Col, Item^.RowNum);
//Now reset the list.
fSortedList.Reset;
end;
end;
function TSortGrid.GetCellDrawState(const ACol, ARow: Longint): TGridDrawState;
function PointInGridRect(Col, Row: Longint; const Rect: TGridRect): Boolean;
begin
Result := (Col >= Rect.Left) and (Col <= Rect.Right) and (Row >= Rect.Top)
and (Row <= Rect.Bottom);
end;
var
DrawState: TGridDrawState;
begin
DrawState:=[];
if (ARow < FixedRows) and (ACol < FixedCols) then Include(DrawState, gdFixed);
if Focused and (ARow = Row) and (ACol = Col) then Include(DrawState, gdFocused);
if PointInGridRect(ACol, ACol, Selection) then Include(DrawState, gdSelected);
Result:=DrawState;
end;
function TSortGrid.SelectCell(ACol, ARow: Longint): Boolean;
begin
Result:=inherited SelectCell(ACol, ARow);
end;
end.
소스가 너무 긴거 아닌가하는 생각이 ....
혹시 짤리면....
참고하세요..
작은 프로그래머 토이가....
|