home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 25 / nopv25.iso / 040A / DELZIP10.ZIP / VCL.ZIP / SORTGRID.PAS next >
Encoding:
Pascal/Delphi Source File  |  1997-04-07  |  40.0 KB  |  1,227 lines

  1. {*******************************************************************************
  2.    Class: TSortGrid
  3.    Copyright ⌐ 1996
  4.    Author: Bill Menees
  5.            bmenees@usit.net
  6.            www.public.usit.net/bmenees
  7.    Modified by Eric W. Engler, Feb 1997
  8.     - fixed a bug in autodetection of type
  9.     - OnBeginSort event was called before the autodetect of type, Moved to after
  10.     - expanded date sort to incl datetime (these are usu compatible in Delphi)
  11.     - added a time sort
  12. *******************************************************************************}
  13. unit SortGrid;
  14.  
  15. interface
  16.  
  17. uses
  18.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  19.   StdCtrls, Grids;
  20.  
  21. type
  22.   TSortStyle = (ssAutomatic, ssAlphabetic, ssNumeric, ssDateTime, ssTime,ssCustom);
  23.   {TSortCompare must return < 0 if Str1 is less than Str2,
  24.    0 if they are equal, and > 0 if Str1 is greater than Str2.}
  25.   TSortCompare = function (const Str1, Str2: String): Integer;
  26.   TSortDirection = (sdAscending, sdDescending);
  27.   {**************************************************************}
  28.   {*** NOTE: These are the options you can set to affect sorting.}
  29.   TSortOptions = record
  30.       SortStyle: TSortStyle;
  31.       SortDirection: TSortDirection;
  32.       SortCaseSensitive: Boolean;
  33.       SortCompare: TSortCompare;  //Used only if SortStyle = ssCustom.
  34.   end;
  35.  
  36.   TSortedListEntry = record
  37.       Str: String;
  38.       RowNum: Longint;
  39.   end;
  40.   PSortedListEntry = ^TSortedListEntry;
  41.   TSortedList = class(TList)
  42.   public
  43.         function GetItem(const i: Integer): PSortedListEntry;
  44.         procedure Reset;
  45.   end;
  46.  
  47.   TCellBevelStyle = (cbNone, cbRaised, cbLowered);
  48.   {**********************************************************}
  49.   {*** NOTE: This is one of the structures in TFormatOptions.}
  50.   TCellBevel = record
  51.       Style: TCellBevelStyle;
  52.       UpperLeftColor: TColor;
  53.       LowerRightColor: TColor;
  54.   end;
  55.   TVertAlignment = (taTopJustify, taBottomJustify, taMiddle);
  56.   {**************************************************}
  57.   {*** NOTE: These are the display options you can set
  58.              for each cell in OnGetCellFormat.}
  59.   TFormatOptions = record
  60.       Brush: TBrush;
  61.       Font: TFont;
  62.       AlignmentHorz: TAlignment;
  63.       AlignmentVert: TVertAlignment;
  64.       Bevel: TCellBevel;
  65.       HideText: Boolean;
  66.   end;
  67.  
  68.   {These are the new events defined for TSortGrid.}
  69.   TFormatDrawCellEvent = procedure(Sender: TObject; Col, Row: Longint;
  70.                           State: TGridDrawState;
  71.                           var FormatOptions: TFormatOptions) of object;
  72.   TClickSortEvent = procedure(Sender: TObject; Col, Row: Longint;
  73.                           var SortOptions: TSortOptions) of object;
  74.   TUpdateGridEvent = procedure(Sender: TObject; Index: Longint) of object;
  75.   TSizeChangedEvent = procedure(Sender: TObject; OldColCount, OldRowCount: Longint) of object;
  76.   TBeginSortEvent = procedure(Sender: TObject; Col: Longint; var SortOptions: TSortOptions) of object;
  77.   TEndSortEvent = procedure(Sender: TObject; Col: Longint) of object;
  78.   TCellValidateEvent = procedure(Sender: TObject; Col, Row: Longint;
  79.                      var Value: String; var Valid: Boolean) of object;
  80.  
  81.   {Here's the main new class: TSortGrid}
  82.   TSortGrid = class(TStringGrid)
  83.   private
  84.     { Private declarations }
  85.     fCaseSensitive: Boolean;
  86.     fSortedList: TSortedList;
  87.     fAlignmentHorz: TAlignment;
  88.     fAlignmentVert: TVertAlignment;
  89.     fClickSorting: Boolean;
  90.     fBevelStyle: TCellBevelStyle;
  91.     fProportionalScrollBars: Boolean;
  92.     fExtendedKeys: Boolean;
  93.     fSorting: Boolean;
  94.     fModified: Boolean;
  95.     fOldCellText: String;
  96.     fOldCol, fOldRow: Longint;
  97.     fOldModifiedValue: Boolean;
  98.     fEntered: Boolean;
  99.  
  100.     fOnGetCellFormat: TFormatDrawCellEvent;
  101.     fOnClickSort: TClickSortEvent;
  102.     fOnRowInsert: TUpdateGridEvent;
  103.     fOnRowDelete: TUpdateGridEvent;
  104.     fOnColumnInsert: TUpdateGridEvent;
  105.     fOnColumnDelete: TUpdateGridEvent;
  106.     fOnColumnWidthsChanged: TNotifyEvent;
  107.     fOnRowHeightsChanged: TNotifyEvent;
  108.     fOnSizeChanged: TSizeChangedEvent;
  109.     fOnBeginSort: TBeginSortEvent;
  110.     fOnEndSort: TEndSortEvent;
  111.     fOnCellValidate: TCellValidateEvent;
  112.  
  113.     procedure SetAlignmentHorz(Value: TAlignment);
  114.     procedure SetAlignmentVert(Value: TVertAlignment);
  115.     procedure SetBevelStyle(Value: TCellBevelStyle);
  116.     procedure WMSize(var Msg: TWMSize); message WM_SIZE;
  117.     procedure SetProportionalScrollBars(Value: Boolean);
  118.   protected
  119.     { Protected declarations }
  120.     procedure ListQuickSort(const ACol: Longint; const SortOptions: TSortOptions); virtual;
  121.     function DetermineSortStyle(const ACol: Longint): TSortStyle; virtual;
  122.     procedure InitializeFormatOptions(var FmtOpts: TFormatOptions);
  123.     procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
  124.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  125.     procedure ColWidthsChanged; override;
  126.     procedure RowHeightsChanged; override;
  127.     procedure SizeChanged(OldColCount, OldRowCount: Longint); override;
  128.     procedure UpdateScrollPage; virtual;
  129.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  130.     procedure SetEditText(ACol, ARow: Longint; const Value: string); override;
  131.     procedure Click; override;
  132.     procedure DoEnter; override;
  133.     procedure DoExit; override;
  134.     procedure InitValidate; virtual;
  135.     procedure KeyPress(var Key: Char); override;
  136. public
  137.     { Public declarations }
  138.     { run time properties }
  139.     property Sorting: Boolean read fSorting default False;
  140.     property Modified: Boolean read fModified write fModified default False;
  141.  
  142.     constructor Create(AOwner: TComponent); override;
  143.     destructor Destroy; override;
  144.  
  145.     procedure MoveTo(ACol, ARow: Longint); virtual;
  146.     procedure Clear; virtual;
  147.     procedure InsertRow(ARow: Longint); virtual;
  148.     procedure InsertColumn(ACol: Longint); virtual;
  149.     procedure DeleteRow(ARow: Longint); virtual;
  150.     procedure DeleteColumn(ACol: Longint); virtual;
  151.     procedure MoveRow(FromIndex, ToIndex: Longint); virtual;
  152.     procedure MoveColumn(FromIndex, ToIndex: Longint); virtual;
  153.     procedure SwapRows(ARow1, ARow2: Longint); virtual;
  154.     procedure SwapColumns(ACol1, ACol2: Longint); virtual;
  155.  
  156.     procedure AutoSizeCol(const ACol: LongInt); virtual;
  157.     procedure AutoSizeColumns(const DoFixedCols: Boolean; const Padding: Integer); virtual;
  158.     procedure SortByColumn(const ACol: LongInt; SortOptions: TSortOptions); virtual;
  159.     function IsCell(const Value: String; var ACol, ARow: Longint): Boolean; virtual;
  160.     procedure LoadFromFile(const FileName: String; const Delimiter: Char); virtual;
  161.     procedure SaveToFile(const FileName: String; const Delimiter: Char); virtual;
  162.     function CanUndoSort: Boolean; virtual;
  163.     procedure UndoSort; virtual;
  164.     function GetCellDrawState(const ACol, ARow: Longint): TGridDrawState;
  165.     function SelectCell(ACol, ARow: Longint): Boolean; override;
  166.     procedure ValidateCell; virtual;
  167.  
  168.   published
  169.     { Published declarations }
  170.     property CaseSensitive: Boolean read fCaseSensitive write fCaseSensitive;
  171.     property AlignmentHorz: TAlignment read fAlignmentHorz write SetAlignmentHorz;
  172.     property AlignmentVert: TVertAlignment read fAlignmentVert write SetAlignmentVert;
  173.     property ClickSorting: Boolean read fClickSorting write fClickSorting;
  174.     property BevelStyle: TCellBevelStyle read fBevelStyle write SetBevelStyle;
  175.     property ProportionalScrollBars: Boolean read fProportionalScrollBars
  176.                                              write SetProportionalScrollBars;
  177.     property ExtendedKeys: Boolean read fExtendedKeys write fExtendedKeys;
  178.  
  179.     { Published events }
  180.     property OnGetCellFormat: TFormatDrawCellEvent read fOnGetCellFormat write fOnGetCellFormat;
  181.     property OnClickSort: TClickSortEvent read fOnClickSort write fOnClickSort;
  182.     property OnRowInsert: TUpdateGridEvent read fOnRowInsert write fOnRowInsert;
  183.     property OnRowDelete: TUpdateGridEvent read fOnRowDelete write fOnRowDelete;
  184.     property OnColumnInsert: TUpdateGridEvent read fOnColumnInsert write fOnColumnInsert;
  185.     property OnColumnDelete: TUpdateGridEvent read fOnColumnDelete write fOnColumnDelete;
  186.     property OnColumnWidthsChanged: TNotifyEvent read fOnColumnWidthsChanged write fOnColumnWidthsChanged;
  187.     property OnRowHeightsChanged: TNotifyEvent read fOnRowHeightsChanged write fOnRowHeightsChanged;
  188.     property OnSizeChanged: TSizeChangedEvent read fOnSizeChanged write fOnSizeChanged;
  189.     property OnBeginSort: TBeginSortEvent read fOnBeginSort write fOnBeginSort;
  190.     property OnEndSort: TEndSortEvent read fOnEndSort write fOnEndSort;
  191.     property OnCellValidate: TCellValidateEvent read fOnCellValidate write fOnCellValidate;
  192.   end;
  193.  
  194. procedure Register;
  195. function StringCompare(const Str1, Str2: String): Integer;
  196. function DateTimeCompare(const Str1, Str2: String): Integer;
  197. function NumericCompare(const Str1, Str2: String): Integer;
  198. function TimeCompare(const Str1, Str2: String): Integer;
  199.  
  200. implementation
  201.  
  202. {$R SortGrid.Res}
  203.  
  204. var
  205.    //This is here for Compare.  I can't pass it as a parameter,
  206.    //and I can't make Compare a method.  So I had to use a global. :-(
  207.    GlobalSortOptions: TSortOptions;
  208.  
  209. {******************************************************************************}
  210. {** Miscellaneous Non-Member Functions                                       **}
  211. {******************************************************************************}
  212. procedure TokenizeGridString(const S: String; const Delimiter: Char; Tokens: TStringList);
  213. var
  214.    i, Len: Integer;
  215.    CurToken: String;
  216. begin
  217.      Tokens.Clear;
  218.      CurToken:='';
  219.      Len:=Length(S);
  220.      for i:=1 to Len do
  221.      begin
  222.           if S[i] = Delimiter then
  223.           begin
  224.                Tokens.Add(CurToken);
  225.                CurToken:='';
  226.           end
  227.           else
  228.               CurToken:=CurToken+S[i];
  229.      end;
  230.      Tokens.Add(CurToken);
  231. end;
  232.  
  233. function StringCompare(const Str1, Str2: String): Integer;
  234. begin
  235.      if Str1 < Str2 then Result:=-1
  236.      else if Str2 < Str1 then Result:=1
  237.      else Result:=0;
  238. end;
  239.  
  240. function DateTimeCompare(const Str1, Str2: String): Integer;
  241. var
  242.    Val1, Val2: TDateTime;
  243. begin
  244.      try
  245.         Val1:=StrToDateTime(Str1);
  246.         Val2:=StrToDateTime(Str2);
  247.         if Val1 < Val2 then Result:=-1
  248.         else if Val2 < Val1 then Result:=1
  249.         else Result:=0;
  250.      except
  251.         on EConvertError do Result:=0;
  252.      end;
  253. end;
  254.  
  255. function TimeCompare(const Str1, Str2: String): Integer;
  256. var
  257.    Val1, Val2: TDateTime;
  258. begin
  259.      try
  260.         Val1:=StrToTime(Str1);
  261.         Val2:=StrToTime(Str2);
  262.         if Val1 < Val2 then Result:=-1
  263.         else if Val2 < Val1 then Result:=1
  264.         else Result:=0;
  265.      except
  266.         on EConvertError do Result:=0;
  267.      end;
  268. end;
  269.  
  270. function NumericCompare(const Str1, Str2: String): Integer;
  271. var
  272.    Val1, Val2: Extended;
  273. begin
  274.      try
  275.         Val1:=StrToFloat(Str1);
  276.         Val2:=StrToFloat(Str2);
  277.         if Val1 < Val2 then Result:=-1
  278.         else if Val2 < Val1 then Result:=1
  279.         else Result:=0;
  280.      except
  281.         on EConvertError do Result:=0;
  282.      end;
  283. end;
  284.  
  285. //This looks at the global variable GlobalSortOptions.
  286. //I hated to use a global, but I can't pass any additional
  287. //parameters to Compare, and I can't make Compare a
  288. //method of an object.  A global seemed the only choice.
  289. function Compare(Item1, Item2: Pointer): Integer;
  290. var
  291.    Entry1, Entry2: PSortedListEntry;
  292. begin
  293.      Entry1:=Item1;
  294.      Entry2:=Item2;
  295.  
  296.      //Handle Case-Insensitivity.
  297.      if not GlobalSortOptions.SortCaseSensitive then
  298.      begin
  299.           Entry1^.Str:=Uppercase(Entry1^.Str);
  300.           Entry2^.Str:=Uppercase(Entry2^.Str);
  301.      end;
  302.  
  303.      //Determine compare type and do the comparison.
  304.      case GlobalSortOptions.SortStyle of
  305.           ssNumeric: Result:=NumericCompare(Entry1^.Str, Entry2^.Str);
  306.           ssDateTime: Result:=DateTimeCompare(Entry1^.Str, Entry2^.Str);
  307.           ssTime: Result:=TimeCompare(Entry1^.Str, Entry2^.Str);
  308.           ssCustom: Result:=GlobalSortOptions.SortCompare(Entry1^.Str, Entry2^.Str);
  309.      else
  310.          Result:=StringCompare(Entry1^.Str, Entry2^.Str);
  311.      end;
  312.  
  313.      //Now, make sure we don't swap the rows if the Keys are equal.
  314.      //If they're equal then we sort by row number.
  315.      if Result = 0 then
  316.      begin
  317.           if Entry1^.RowNum < Entry2^.RowNum then Result:=-1
  318.           else if Entry1^.RowNum > Entry2^.RowNum then Result:=1
  319.           else Result:=0; //Sometimes an item does get compared to itself.
  320.      end
  321.      else //Reverse polarity if descending sort.
  322.          if GlobalSortOptions.SortDirection = sdDescending then
  323.             Result:=-1*Result;
  324. end;
  325.  
  326. //I put TSortGrid on the 'Additional' page because
  327. //I like having it with the other grids.
  328. procedure Register;
  329. begin
  330.      RegisterComponents('Additional', [TSortGrid]);
  331. end;
  332.  
  333. {******************************************************************************}
  334. {** Public Members for TSortedList                                           **}
  335. {******************************************************************************}
  336. function TSortedList.GetItem(const i: Integer): PSortedListEntry;
  337. begin
  338.      //Cast the pointer.
  339.      Result:=PSortedListEntry(Items[i]);
  340. end;
  341.  
  342. procedure TSortedList.Reset;
  343. var
  344.    i: Integer;
  345. begin
  346.      //Dispose of anything in the list first.
  347.      for i:=0 to Count-1 do
  348.          if Items[i] <> nil then
  349.             Dispose(Items[i]);
  350.      //Now clear the list.
  351.      Clear;
  352. end;
  353.  
  354. {******************************************************************************}
  355. {** Private Members for TSortGrid                                            **}
  356. {******************************************************************************}
  357. procedure TSortGrid.SetAlignmentHorz(Value: TAlignment);
  358. begin
  359.      fAlignmentHorz:=Value;
  360.      Invalidate;
  361. end;
  362.  
  363. procedure TSortGrid.SetAlignmentVert(Value: TVertAlignment);
  364. begin
  365.      fAlignmentVert:=Value;
  366.      Invalidate;
  367. end;
  368.  
  369. procedure TSortGrid.SetBevelStyle(Value: TCellBevelStyle);
  370. begin
  371.      fBevelStyle:=Value;
  372.      Invalidate;
  373. end;
  374.  
  375. procedure TSortGrid.WMSize(var Msg: TWMSize);
  376. begin
  377.      inherited;
  378.      UpdateScrollPage;
  379. end;
  380.  
  381. procedure TSortGrid.SetProportionalScrollBars(Value: Boolean);
  382. begin
  383.      fProportionalScrollBars:=Value;
  384.      UpdateScrollPage;
  385. end;
  386.  
  387. {******************************************************************************}
  388. {** Protected Members for TSortGrid                                          **}
  389. {******************************************************************************}
  390. procedure TSortGrid.ListQuickSort(const ACol: Longint; const SortOptions: TSortOptions);
  391. var
  392.    i: Integer;
  393.    Item: PSortedListEntry;
  394.    BufferGrid: TStringGrid;
  395. {$IFDEF DEBUG}
  396.    Order: String;
  397. {$ENDIF}
  398. begin
  399.      //Let everyone know we're sorting.
  400.      fSorting:=True;
  401.      try
  402.         //Get rid of any old entries in the sorted list.
  403.         fSortedList.Reset;
  404.  
  405.         //Set the sort options for the list.
  406.         //"Compare" can only look at GlobalSortOptions.
  407.         GlobalSortOptions:=SortOptions;
  408.  
  409. {$IFDEF DEBUG}
  410.         if SortOptions.SortDirection=sdAscending then
  411.            Order:='ascending'
  412.         else
  413.            Order:='descending';
  414.         case GlobalSortOptions.SortStyle of
  415.           ssNumeric: ShowMessage('Sorting Column ' + IntToStr(ACol) + ', numeric, ' + Order);
  416.           ssDateTime: ShowMessage('Sorting Column ' + IntToStr(ACol) + ', datetime, ' + Order);
  417.           ssTime:    ShowMessage('Sorting Column ' + IntToStr(ACol) + ', time, ' + Order);
  418.           ssCustom:  ShowMessage('Sorting Column ' + IntToStr(ACol) + ', custom, ' + Order);
  419.         else
  420.           ShowMessage('Sorting Column ' + IntToStr(ACol) + ', alpha, ' + Order);
  421.        end;
  422. {$ENDIF}
  423.  
  424.         //Insert the Row Number and Key (Str) into
  425.         for i:=FixedRows to RowCount-1 do
  426.         begin
  427.              New(Item);
  428.              Item^.RowNum:=i;
  429.              Item^.Str:=Cells[ACol, i];
  430.              fSortedList.Add(Item);
  431.         end;
  432.  
  433.         //Quick Sort the list by key string.
  434.         //Then the row numbers will indicate where
  435.         //each row should be placed.
  436.         //E.g. If list item 0 contains a RowNum of 4 then
  437.         //row 4 should be the first row (position 0).
  438.         fSortedList.Sort(Compare);
  439.  
  440.         //Now rearrange the rows of the grid in sorted order.
  441.         //This is a fast but space inefficient way to do it.
  442.         //First, create a buffer grid and size it correctly.
  443.         BufferGrid:=TStringGrid.Create(Self);
  444.         try
  445.            BufferGrid.ColCount:=ColCount;
  446.            BufferGrid.RowCount:=RowCount;
  447.            //Copy the rows to the buffer grid in sorted order.
  448.            for i:=0 to fSortedList.Count-1 do
  449.            begin
  450.                 Item:=fSortedList.GetItem(i);
  451.                 BufferGrid.Rows[i+FixedRows].Assign(Rows[Item^.RowNum]);
  452.            end;
  453.            //Now put the rows back into the original grid.
  454.            for i:=FixedRows to RowCount-1 do
  455.            begin
  456.                 Rows[i].Assign(BufferGrid.Rows[i]);
  457.            end;
  458.         finally
  459.            BufferGrid.Free;
  460.         end;
  461.  
  462.         //Now put the selection back on the right row.
  463.         for i:=0 to fSortedList.Count-1 do
  464.         begin
  465.              Item:=fSortedList.GetItem(i);
  466.              if Item^.RowNum = Row then
  467.              begin
  468.                   MoveTo(Col, i+FixedRows);
  469.                   Break;
  470.              end;
  471.         end;
  472.  
  473.      finally
  474.         //Make sure we get this turned off.
  475.         fSorting:=False;
  476.      end;
  477. end;
  478.  
  479. //This function tries to determine the best sort style
  480. //for a column.  If all the entries can be converted to
  481. //numbers, a numeric sort is returned.  If they can all
  482. //be converted to dates, a date sort is returned.  If time,
  483. //then a time sort is returned,
  484. //Otherwise, an alphabetic sort is returned.
  485. function TSortGrid.DetermineSortStyle(const ACol: Longint): TSortStyle;
  486. var
  487.    i: Integer;
  488.    DoNumeric, DoDateTime, DoTime: Boolean;
  489. begin
  490.      DoNumeric:=True;
  491.      DoDateTime:=True;
  492.      DoTime:=True;
  493.  
  494.      //Note: We only go through the rows once.
  495.      //This code depends on the fact that no
  496.      //entry can be both a date and number.
  497.      for i:=FixedRows to RowCount-1 do
  498.      begin
  499.           if DoNumeric then
  500.           begin
  501.                try
  502.                   StrToFloat(Cells[ACol, i]);
  503.                except
  504.                   on EConvertError do
  505.                      DoNumeric:=False;
  506.                end;
  507.           end;
  508.  
  509.           if DoTime then
  510.           begin
  511.                try
  512.                   StrToTime(Cells[ACol, i]);
  513.                except
  514.                   on EConvertError do
  515.                      DoTime:=False;
  516.                end;
  517.           end;
  518.  
  519.           if DoDateTime then
  520.           begin
  521.                try
  522.                   StrToDateTime(Cells[ACol, i]);
  523.                except
  524.                   on EConvertError do
  525.                      DoDateTime:=False;
  526.                end;
  527.           end;
  528.      end;
  529.  
  530.      if DoNumeric then Result := ssNumeric
  531.      else if DoDateTime then Result := ssDateTime
  532.      else if DoTime then Result := ssTime
  533.      else Result := ssAlphabetic;
  534. end;
  535.  
  536. procedure TSortGrid.InitializeFormatOptions(var FmtOpts: TFormatOptions);
  537. begin
  538.      //Setup good defaults for FormatOptions.
  539.      FmtOpts.HideText:=False;
  540.      FmtOpts.Font:=Canvas.Font;
  541.      FmtOpts.Brush:=Canvas.Brush;
  542.      FmtOpts.AlignmentHorz:=AlignmentHorz;
  543.      FmtOpts.AlignmentVert:=AlignmentVert;
  544.      FmtOpts.Bevel.Style:=BevelStyle;
  545.      //Set defaults for the bevel colors.
  546.      case BevelStyle of
  547.           cbRaised:
  548.           begin
  549.                FmtOpts.Bevel.UpperLeftColor:=clBtnHighlight;
  550.                FmtOpts.Bevel.LowerRightColor:=clBtnShadow;
  551.           end;
  552.           cbLowered:
  553.           begin
  554.                FmtOpts.Bevel.UpperLeftColor:=clBtnShadow;
  555.                FmtOpts.Bevel.LowerRightColor:=clBtnHighlight;
  556.           end;
  557.      else
  558.          FmtOpts.Bevel.UpperLeftColor:=clWindow;
  559.          FmtOpts.Bevel.LowerRightColor:=clWindow;
  560.      end;
  561. end;
  562.  
  563. procedure TSortGrid.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
  564. var
  565.    xOffset, yOffset: Integer;
  566.    FmtOpts: TFormatOptions;
  567. begin
  568.      InitializeFormatOptions(FmtOpts);
  569.  
  570.      //Now do the OnGetCellFormat event if necessary.
  571.      if Assigned(fOnGetCellFormat) then
  572.         fOnGetCellFormat(Self, ACol, ARow, AState, FmtOpts);
  573.  
  574.      if DefaultDrawing then
  575.      begin
  576.           //Calculate horizontal offset.
  577.           case FmtOpts.AlignmentHorz of
  578.                taRightJustify:
  579.                    xOffset:=ARect.Right-ARect.Left-Canvas.TextWidth(Cells[ACol, ARow])-2;
  580.                taCenter:
  581.                    xOffset:=(ARect.Right-ARect.Left-Canvas.TextWidth(Cells[ACol, ARow])) div 2;
  582.           else
  583.               xOffset:=2;
  584.           end;
  585.  
  586.           //Calculate vertical offset.
  587.           case FmtOpts.AlignmentVert of
  588.                taBottomJustify:
  589.                    yOffset:=ARect.Bottom-ARect.Top-Canvas.TextHeight(Cells[ACol, ARow])-3;
  590.                taMiddle:
  591.                    yOffset:=(ARect.Bottom-ARect.Top-Canvas.TextHeight(Cells[ACol, ARow])) div 2;
  592.           else
  593.               yOffset:=2;
  594.           end;
  595.  
  596.           //Now do the text drawing.
  597.           if not FmtOpts.HideText then
  598.              Canvas.TextRect(ARect, ARect.Left+xOffset, ARect.Top+yOffset, Cells[ACol, ARow])
  599.           else
  600.               Canvas.TextRect(ARect, ARect.Left+xOffset, ARect.Top+yOffset, '');
  601.  
  602.           //Draw Bevel.
  603.           if (FmtOpts.Bevel.Style <> cbNone) and
  604.              (ACol >= FixedCols) and (ARow >= FixedRows) then
  605.           begin
  606.                //Draw from bottom-most lines out to mimic behaviour of
  607.                //fixed cells when FixedXXXXLine is toggled.
  608.                with ARect do
  609.                begin
  610.                     if goFixedVertLine in Options then
  611.                     begin
  612.                          Canvas.Pen.Color := FmtOpts.Bevel.LowerRightColor;
  613.                          Canvas.PolyLine([Point(Right-1, Top), Point(Right-1, Bottom)]);
  614.                     end;
  615.                     if goFixedHorzLine in Options then
  616.                     begin
  617.                          Canvas.Pen.Color := FmtOpts.Bevel.LowerRightColor;
  618.                          Canvas.PolyLine([Point(Left, Bottom-1), Point(Right, Bottom-1)]);
  619.                     end;
  620.                     if goFixedVertLine in Options then
  621.                     begin
  622.                          Canvas.Pen.Color := FmtOpts.Bevel.UpperLeftColor;
  623.                          Canvas.PolyLine([Point(Left, Top), Point(Left, Bottom)]);
  624.                     end;
  625.                     if goFixedHorzLine in Options then
  626.                     begin
  627.                          Canvas.Pen.Color := FmtOpts.Bevel.UpperLeftColor;
  628.                          Canvas.PolyLine([Point(Left, Top), Point(Right, Top)]);
  629.                     end;
  630.                end;
  631.           end;
  632.      end
  633.      else
  634.          inherited DrawCell(ACol, ARow, ARect, AState);
  635. end;
  636.  
  637. procedure TSortGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  638. var
  639.    Point: TPoint;
  640.    ACol, ARow: LongInt;
  641.    SortOptions: TSortOptions;
  642.    CurrentCursor: TCursor;
  643. begin
  644.      //Make sure we're not sizing and have a header row.
  645.      if Focused and
  646.         ClickSorting and
  647.         (fGridState = gsNormal) and
  648.         (FixedRows >= 1) and
  649.         (Shift = []) then
  650.      begin
  651.           Point.x:=X;
  652.           Point.y:=Y;
  653.           MouseToCell(Point.x, Point.y, ACol, ARow);
  654.           //Make sure they clicked a fixed row.
  655.           if (ARow >= 0) and (ARow <= (FixedRows-1)) then
  656.           begin
  657.                SortOptions.SortStyle:=ssAutomatic;
  658.                if Button = mbRight then
  659.                   SortOptions.SortDirection:=sdDescending
  660.                else
  661.                   SortOptions.SortDirection:=sdAscending;
  662.  
  663.                { EWE: Set case sensitivity here }
  664.                SortOptions.SortCaseSensitive:=fCaseSensitive;
  665.  
  666.                SortOptions.SortCompare:=nil;
  667.                if Assigned(fOnClickSort) then
  668.                   fOnClickSort(Self, ACol, ARow, SortOptions);
  669.                CurrentCursor:=Screen.Cursor;
  670.                try
  671.                   Screen.Cursor:=crHourglass;
  672.                   SortByColumn(ACol, SortOptions);
  673.                finally
  674.                   Screen.Cursor:=CurrentCursor;
  675.                end;
  676.           end;
  677.      end;
  678.      inherited MouseUp(Button, Shift, X, Y);
  679. end;
  680.  
  681. procedure TSortGrid.ColWidthsChanged;
  682. begin
  683.      inherited ColWidthsChanged;
  684.      if Assigned(fOnColumnWidthsChanged) then
  685.         fOnColumnWidthsChanged(Self);
  686.      UpdateScrollPage;
  687. end;
  688.  
  689. procedure TSortGrid.RowHeightsChanged;
  690. begin
  691.      inherited RowHeightsChanged;
  692.      if Assigned(fOnRowHeightsChanged) then
  693.         fOnRowHeightsChanged(Self);
  694.      UpdateScrollPage;
  695. end;
  696.  
  697. procedure TSortGrid.SizeChanged(OldColCount, OldRowCount: Longint);
  698. begin
  699.      inherited SizeChanged(OldColCount, OldRowCount);
  700.      if Assigned(fOnSizeChanged) then
  701.         fOnSizeChanged(Self, OldColCount, OldRowCount);
  702.      UpdateScrollPage;
  703. end;
  704.  
  705. procedure TSortGrid.UpdateScrollPage;
  706.     function LMax(const A, B: Longint): Longint;
  707.     begin
  708.          Result:=B;
  709.          if A > B then Result:=A;
  710.     end;
  711. var
  712.    SI: TScrollInfo;
  713. begin
  714.      {Make the scroll bar(s) proportional.}
  715.      {To do this correctly, I should sum colwidths and rowheights,
  716.      but I just approximate by basing the proportion on visible rows or cols
  717.      divided by row or col count...}
  718.      {Also, I can't really figure out Borland's scroll bar range and position
  719.      scheme.  Thus, sometimes when you click on the end of the scroll bar, you
  720.      still have to scroll farther with the arrows to actually get to the end
  721.      of the grid.  If anyone knows how to fix this, PLEASE let me know...}
  722.      if (ScrollBars = ssVertical) or (ScrollBars = ssBoth) then
  723.      begin
  724.            SI.cbSize:=sizeof(SI);
  725.            SI.fMask:=SIF_PAGE or SIF_POS or SIF_RANGE;
  726.            GetScrollInfo(Handle, SB_VERT, SI);
  727.            SI.fMask:=SIF_PAGE;
  728.            if ProportionalScrollBars then
  729.            begin
  730.                 SI.nPage:=LMax(Round(((1.0*(VisibleRowCount+FixedRows))/RowCount)*(SI.nMax-SI.nMin+1)), 1)
  731.            end
  732.            else
  733.                SI.nPage:=0;
  734.            SetScrollInfo(Handle, SB_VERT, SI, True);
  735.      end;
  736.      if (ScrollBars = ssHorizontal) or (ScrollBars = ssBoth) then
  737.      begin
  738.            SI.cbSize:=sizeof(SI);
  739.            SI.fMask:=SIF_PAGE or SIF_POS or SIF_RANGE;
  740.            GetScrollInfo(Handle, SB_HORZ, SI);
  741.            SI.fMask:=SIF_PAGE;
  742.            if ProportionalScrollBars then
  743.            begin
  744.                 SI.nPage:=LMax(Round(((1.0*(VisibleColCount+FixedCols))/ColCount)*(SI.nMax-SI.nMin+1)), 1)
  745.            end
  746.            else
  747.                SI.nPage:=0;
  748.            SetScrollInfo(Handle, SB_HORZ, SI, True);
  749.      end;
  750. end;
  751.  
  752. procedure TSortGrid.KeyDown(var Key: Word; Shift: TShiftState);
  753. begin
  754.      inherited KeyDown(Key, Shift);
  755.      if ExtendedKeys and not EditorMode then
  756.      begin
  757.           if Shift = [ssCtrl] then
  758.           begin
  759.                case Key of
  760.                     VK_INSERT: InsertRow(Row);
  761.                     VK_DELETE: if RowCount > (FixedRows+1) then DeleteRow(Row);
  762.                end;
  763.           end
  764.           else if Shift = [ssCtrl, ssShift] then
  765.           begin
  766.                case Key of
  767.                     VK_INSERT: InsertColumn(Col);
  768.                     VK_DELETE: if ColCount > (FixedCols+1) then DeleteColumn(Col);
  769.                end;
  770.           end;
  771.      end;
  772. end;
  773.  
  774. procedure TSortGrid.SetEditText(ACol, ARow: Longint; const Value: string);
  775. begin
  776.      try
  777.         if Value <> Cells[ACol, ARow] then Modified:=True;
  778.      finally
  779.             inherited SetEditText(ACol, ARow, Value);
  780.      end;
  781. end;
  782.  
  783. procedure TSortGrid.Click;
  784. begin
  785.      try
  786.         inherited Click;
  787.      finally
  788.         if fEntered then ValidateCell;
  789.      end;
  790. end;
  791.  
  792. procedure TSortGrid.DoEnter;
  793. begin
  794.      try
  795.         inherited DoEnter;
  796.         fEntered:=True;
  797.      finally
  798.         InitValidate;
  799.      end;
  800. end;
  801.  
  802. procedure TSortGrid.DoExit;
  803. begin
  804.      try
  805.         Click;
  806.      finally
  807.         inherited DoExit;
  808.         fEntered:=False;
  809.      end;
  810. end;
  811.  
  812. procedure TSortGrid.InitValidate;
  813. begin
  814.      fOldCol:=Col;
  815.      fOldRow:=Row;
  816.      fOldCellText:=Cells[fOldCol, fOldRow];
  817.      fOldModifiedValue:=Modified;
  818. end;
  819.  
  820. {******************************************************************************}
  821. {** Public Members for TSortGrid                                             **}
  822. {******************************************************************************}
  823.  
  824. constructor TSortGrid.Create(AOwner: TComponent);
  825. begin
  826.      inherited Create(AOwner);
  827.      fSortedList:=TSortedList.Create;
  828.      fCaseSensitive:=False;  { dflt to no case sensitivity }
  829.      fAlignmentHorz:=taLeftJustify;
  830.      fAlignmentVert:=taTopJustify;
  831.      fClickSorting:=True;
  832.      fBevelStyle:=cbNone;
  833.      fProportionalScrollBars:=True;
  834.      fExtendedKeys:=False;
  835.      fSorting:=True;
  836.      fModified:=False;
  837.      fEntered:=False;
  838.  
  839.      InitValidate;
  840. end;
  841.  
  842. destructor TSortGrid.Destroy;
  843. begin
  844.      fSortedList.Reset;
  845.      fSortedList.Free;
  846.      inherited Destroy;
  847. end;
  848.  
  849. procedure TSortGrid.ValidateCell;
  850. var
  851.    Value: String;
  852.    Valid: Boolean;
  853. begin
  854.      if fOldCellText<>Cells[fOldCol, fOldRow] then
  855.      begin
  856.           Value:=Cells[fOldCol, fOldRow];
  857.           Valid:=True;
  858.           if Assigned(fOnCellValidate) then
  859.              fOnCellValidate(Self, fOldCol, fOldRow, Value, Valid);
  860.           //Since Value is also a VAR parameter, we always
  861.           //use it if it was changed in OnCellValidate.
  862.           if not Valid then
  863.           begin
  864.                if Value <> Cells[fOldCol, fOldRow] then
  865.                   Cells[fOldCol, fOldRow]:=Value
  866.                else
  867.                    Cells[fOldCol, fOldRow]:=fOldCellText;
  868.                Modified:=fOldModifiedValue;
  869.           end
  870.           else if Value <> Cells[fOldCol, fOldRow] then
  871.                Cells[fOldCol, fOldRow]:=Value;
  872.      end;
  873.      InitValidate;
  874. end;
  875.  
  876. //AutoSizes the ACol column.
  877. procedure TSortGrid.AutoSizeCol(const ACol: LongInt);
  878. var
  879.    MaxWidth, TextW, i: Integer;
  880.    FmtOpts: TFormatOptions;
  881. begin
  882.      //Resize the column to display the largest value.
  883.      MaxWidth:=0;
  884.      Canvas.Font:=Font;
  885.      for i:=0 to RowCount-1 do
  886.      begin
  887.           InitializeFormatOptions(FmtOpts);
  888.           if Assigned(fOnGetCellFormat) then
  889.              fOnGetCellFormat(Self, Col, i, GetCellDrawState(ACol, i), FmtOpts);
  890.           Canvas.Font:=FmtOpts.Font;
  891.           TextW:=Canvas.TextWidth(Cells[ACol, i]);
  892.           if TextW > MaxWidth then MaxWidth:=TextW;
  893.      end;
  894.      ColWidths[ACol]:=MaxWidth+Canvas.TextWidth('x');
  895. end;
  896.  
  897. //AutoSizes ALL the variable columns and optionally the fixed columns.
  898. procedure TSortGrid.AutoSizeColumns(const DoFixedCols: Boolean; const Padding: Integer);
  899. var
  900.    i: Integer;
  901. begin
  902.      if DoFixedCols then
  903.         for i:=0 to FixedCols-1 do
  904.         begin
  905.              AutoSizeCol(i);
  906.              if Padding <> 0 then
  907.                 ColWidths[i]:=ColWidths[i]+Padding;
  908.         end;
  909.      for i:=FixedCols to ColCount-1 do
  910.      begin
  911.           AutoSizeCol(i);
  912.           if Padding <> 0 then
  913.              ColWidths[i]:=ColWidths[i]+Padding;
  914.      end;
  915. end;
  916.  
  917. //Sorts the variable rows using Column ACol as a key
  918. procedure TSortGrid.SortByColumn(const ACol: LongInt; SortOptions: TSortOptions);
  919. begin
  920.      //Don't sort while in edit mode.
  921.      if not EditorMode then
  922.      begin
  923.           //If there's only one row we don't need to do anything.
  924.           if RowCount > (FixedRows+1) then
  925.           begin
  926.                //Now we do the Automatic sorting determination.
  927.                if SortOptions.SortStyle = ssAutomatic then
  928.                   SortOptions.SortStyle:=DetermineSortStyle(ACol);
  929.  
  930.                //Call the OnBeginSort event.
  931.                if Assigned(fOnBeginSort) then fOnBeginSort(Self, ACol, SortOptions);
  932.  
  933.                //Quick Sort column ACol.
  934.                ListQuickSort(ACol, SortOptions);
  935.  
  936.                //Call the OnEndSort event.
  937.                if Assigned(fOnEndSort) then fOnEndSort(Self, ACol);
  938.           end;
  939.      end;
  940. end;
  941.  
  942. procedure TSortGrid.InsertRow(ARow: Longint);
  943. begin
  944.      RowCount:=RowCount+1;
  945.      MoveRow(RowCount-1, ARow);
  946.      Rows[ARow].Clear;
  947.      Row:=ARow;
  948.      if Assigned(fOnRowInsert) then fOnRowInsert(Self, ARow);
  949. end;
  950.  
  951. procedure TSortGrid.InsertColumn(ACol: Longint);
  952. begin
  953.      ColCount:=ColCount+1;
  954.      MoveColumn(ColCount-1, ACol);
  955.      Cols[ACol].Clear;
  956.      Col:=ACol;
  957.      if Assigned(fOnColumnInsert) then fOnColumnInsert(Self, ACol);
  958. end;
  959.  
  960. procedure TSortGrid.DeleteRow(ARow: Longint);
  961. var
  962.    ASE: Boolean;
  963. begin
  964.      Rows[ARow].Clear;
  965.      {If goAlwaysShowEditor is enabled then DeleteRow
  966.       and MoveRow leave the caret past the last row or
  967.       in one of the fixed rows.  So I turn it off before
  968.       the delete and then back on after to get it
  969.       working correctly.}
  970.      ASE:=False;
  971.      if goAlwaysShowEditor in Options then
  972.      begin
  973.           Options:=Options-[goAlwaysShowEditor];
  974.           ASE:=True;
  975.      end;
  976.      inherited DeleteRow(ARow);
  977.      if ASE then Options:=Options+[goAlwaysShowEditor];
  978.      if Assigned(fOnRowDelete) then fOnRowDelete(Self, ARow);
  979. end;
  980.  
  981. procedure TSortGrid.DeleteColumn(ACol: Longint);
  982. var
  983.    ASE: Boolean;
  984. begin
  985.      Cols[ACol].Clear;
  986.      //See DeleteRow for comments...
  987.      ASE:=False;
  988.      if goAlwaysShowEditor in Options then
  989.      begin
  990.           Options:=Options-[goAlwaysShowEditor];
  991.           ASE:=True;
  992.      end;
  993.      inherited DeleteColumn(ACol);
  994.      if ASE then Options:=Options+[goAlwaysShowEditor];
  995.      if Assigned(fOnColumnDelete) then fOnColumnDelete(Self, ACol);
  996. end;
  997.  
  998. procedure TSortGrid.MoveRow(FromIndex, ToIndex: Longint);
  999. var
  1000.    ASE: Boolean;
  1001. begin
  1002.      //See DeleteRow for comments...
  1003.      ASE:=False;
  1004.      if goAlwaysShowEditor in Options then
  1005.      begin
  1006.           Options:=Options-[goAlwaysShowEditor];
  1007.           ASE:=True;
  1008.      end;
  1009.      inherited MoveRow(FromIndex, ToIndex);
  1010.      if ASE then Options:=Options+[goAlwaysShowEditor];
  1011. end;
  1012.  
  1013. procedure TSortGrid.MoveColumn(FromIndex, ToIndex: Longint);
  1014. var
  1015.    ASE: Boolean;
  1016. begin
  1017.      //See DeleteRow for comments...
  1018.      ASE:=False;
  1019.      if goAlwaysShowEditor in Options then
  1020.      begin
  1021.           Options:=Options-[goAlwaysShowEditor];
  1022.           ASE:=True;
  1023.      end;
  1024.      inherited MoveColumn(FromIndex, ToIndex);
  1025.      if ASE then Options:=Options+[goAlwaysShowEditor];
  1026. end;
  1027.  
  1028. //The logic gets around a weird case where you swap with the last row.
  1029. procedure TSortGrid.SwapRows(ARow1, ARow2: Longint);
  1030. begin
  1031.      if ARow1 < ARow2 then
  1032.      begin
  1033.           MoveRow(ARow2, ARow1);
  1034.           MoveRow(ARow1+1, ARow2);
  1035.      end
  1036.      else if ARow2 < ARow1 then
  1037.      begin
  1038.           MoveRow(ARow1, ARow2);
  1039.           MoveRow(ARow2+1, ARow1);
  1040.      end;
  1041. end;
  1042.  
  1043. //The logic gets around a weird case where you swap with the last column.
  1044. procedure TSortGrid.SwapColumns(ACol1, ACol2: Longint);
  1045. begin
  1046.      if ACol1 < ACol2 then
  1047.      begin
  1048.           MoveColumn(ACol2, ACol1);
  1049.           MoveColumn(ACol1+1, ACol2);
  1050.      end
  1051.      else if ACol2 < ACol1 then
  1052.      begin
  1053.           MoveColumn(ACol1, ACol2);
  1054.           MoveColumn(ACol2+1, ACol1);
  1055.      end;
  1056. end;
  1057.  
  1058. //Moves the selected cell to (ACol, ARow) and makes it visible.
  1059. procedure TSortGrid.MoveTo(ACol, ARow: Longint);
  1060. begin
  1061.      if ACol < FixedCols then ACol:=FixedCols;
  1062.      if ARow < FixedRows then ARow:=FixedRows;
  1063.      if SelectCell(ACol, ARow) then
  1064.      begin
  1065.           Col:=ACol;
  1066.           Row:=ARow;
  1067.           MoveColRow(ACol, ARow, True, True);
  1068.      end;
  1069. end;
  1070.  
  1071. //Clears the grid.
  1072. procedure TSortGrid.Clear;
  1073. var
  1074.   i: Longint;
  1075. begin
  1076.      for i:= 0 to (ColCount-1) do
  1077.      begin
  1078.           Cols[i].Clear;
  1079.      end;
  1080.      Modified:=False;
  1081. end;
  1082.  
  1083. //Finds a string in the grid.
  1084. //It searches by column and returns the first instance it finds.
  1085. function TSortGrid.IsCell(const Value: String; var ACol, ARow: Longint): Boolean;
  1086. var
  1087.   i, Place: Longint;
  1088. begin
  1089.      Result := False;
  1090.      for i := 0 to ColCount-1 do
  1091.      begin
  1092.           Place:=Cols[i].IndexOf(Value);
  1093.           if Place >= 0 then
  1094.           begin
  1095.                ARow := Place;
  1096.                ACol := i;
  1097.                Result := True;
  1098.                break;
  1099.           end;
  1100.      end;
  1101. end;
  1102.  
  1103. procedure TSortGrid.LoadFromFile(const FileName: String; const Delimiter: Char);
  1104. var
  1105.    r: Longint;
  1106.    Lines, Fields: TStringList;
  1107. begin
  1108.      Lines:=TStringList.Create;
  1109.      Fields:=TStringList.Create;
  1110.      try
  1111.         Clear;
  1112.  
  1113.         Lines.LoadFromFile(FileName);
  1114.         RowCount:=Lines.Count;
  1115.         ColCount:=FixedCols+1;
  1116.         for r:=0 to Lines.Count-1 do
  1117.         begin
  1118.              TokenizeGridString(Lines[r], Delimiter, Fields);
  1119.              if Fields.Count > ColCount then ColCount:=Fields.Count;
  1120.              Rows[r].Assign(Fields);
  1121.         end;
  1122.      finally
  1123.         Fields.Free;
  1124.         Lines.Free;
  1125.      end;
  1126. end;
  1127.  
  1128. procedure TSortGrid.SaveToFile(const FileName: String; const Delimiter: Char);
  1129. var
  1130.    r, c: Longint;
  1131.    BufStr: String;
  1132.    Lines: TStringList;
  1133. begin
  1134.      Lines:=TStringList.Create;
  1135.      try
  1136.         Lines.Clear;
  1137.         for r:=0 to RowCount-1 do
  1138.         begin
  1139.              BufStr:='';
  1140.              for c:=0 to ColCount-1 do
  1141.              begin
  1142.                   BufStr:=BufStr+Cells[c, r];
  1143.                   if c<>(ColCount-1) then BufStr:=BufStr+Delimiter;
  1144.              end;
  1145.              Lines.Add(BufStr);
  1146.         end;
  1147.         Lines.SaveToFile(FileName);
  1148.      finally
  1149.             Lines.Free;
  1150.      end;
  1151. end;
  1152.  
  1153. function TSortGrid.CanUndoSort: Boolean;
  1154. begin
  1155.      //We can only undo a sort if we still have exactly the
  1156.      //same number of rows that we did when we sorted.
  1157.      Result:=(fSortedList.Count = (RowCount-FixedRows));
  1158.      if Result = False then fSortedList.Reset;
  1159. end;
  1160.  
  1161. procedure TSortGrid.UndoSort;
  1162. var
  1163.    BufferGrid: TStringGrid;
  1164.    Item: PSortedListEntry;
  1165.    i: Integer;
  1166. begin
  1167.      if CanUndoSort then
  1168.      begin
  1169.           BufferGrid:=TStringGrid.Create(Self);
  1170.           try
  1171.              BufferGrid.ColCount:=ColCount;
  1172.              BufferGrid.RowCount:=RowCount;
  1173.              //Copy the rows to the buffer grid in the current order.
  1174.              for i:=FixedRows to RowCount-1 do
  1175.              begin
  1176.                   BufferGrid.Rows[i].Assign(Rows[i]);
  1177.              end;
  1178.              //Now put the rows back into the original grid in the old order.
  1179.              for i:=0 to fSortedList.Count-1 do
  1180.              begin
  1181.                   Item:=fSortedList.GetItem(i);
  1182.                   Rows[Item^.RowNum].Assign(BufferGrid.Rows[i+FixedRows]);
  1183.              end;
  1184.           finally
  1185.                  BufferGrid.Free;
  1186.           end;
  1187.  
  1188.           //Now put the selection back on the right row.
  1189.           Item:=fSortedList.GetItem(Row-FixedRows);
  1190.           MoveTo(Col, Item^.RowNum);
  1191.  
  1192.           //Now reset the list.
  1193.           fSortedList.Reset;
  1194.      end;
  1195. end;
  1196.  
  1197. function TSortGrid.GetCellDrawState(const ACol, ARow: Longint): TGridDrawState;
  1198.     function PointInGridRect(Col, Row: Longint; const Rect: TGridRect): Boolean;
  1199.     begin
  1200.       Result := (Col >= Rect.Left) and (Col <= Rect.Right) and (Row >= Rect.Top)
  1201.         and (Row <= Rect.Bottom);
  1202.     end;
  1203. var
  1204.    DrawState: TGridDrawState;
  1205. begin
  1206.      DrawState:=[];
  1207.      if (ARow < FixedRows) and (ACol < FixedCols) then Include(DrawState, gdFixed);
  1208.      if Focused and (ARow = Row) and (ACol = Col) then Include(DrawState, gdFocused);
  1209.      if PointInGridRect(ACol, ACol, Selection) then Include(DrawState, gdSelected);
  1210.      Result:=DrawState;
  1211. end;
  1212.  
  1213. function TSortGrid.SelectCell(ACol, ARow: Longint): Boolean;
  1214. begin
  1215.      Result:=inherited SelectCell(ACol, ARow);
  1216. end;
  1217.  
  1218. procedure TSortGrid.KeyPress(var Key: Char);
  1219. begin
  1220.      //I have to do this here because KeyDown doesn't get called
  1221.      //when the enter key is pressed in the inplace editor.
  1222.      if Key = #13 then ValidateCell;
  1223.      inherited KeyPress(Key);
  1224. end;
  1225.  
  1226. end.
  1227.