Browse Source

Refactor: major re-work of TVisualGrid and other changes
- de-coupled grid columns into TVisualColumn & TDataColumn
- added cell rendering support
- added default cell renderers
WARNING: may contain bugs, will resolve in future commits if any.

Herman Schoenfeld 7 years ago
parent
commit
eec328e1aa

+ 113 - 120
src/core/UDataSources.pas

@@ -6,13 +6,25 @@ unit UDataSources;
 interface
 
 uses
-  Classes, SysUtils, UAccounts, UNode, UBlockchain, UCommon, UConst, UCommon.Data, UCommon.Collections, Generics.Collections, Generics.Defaults, syncobjs;
+  Classes, SysUtils, UAccounts, UNode, UBlockchain, UCommon, UMemory, UConst, UCommon.Data, UCommon.Collections, Generics.Collections, Generics.Defaults, syncobjs;
 
 type
 
+  { TAccountsDataSourceBase }
+
+  TAccountsDataSourceBase = class(TCustomDataSource<TAccount>)
+    protected
+      function GetItemDisposePolicy : TDisposePolicy; override;
+      function GetColumns : TDataColumns;  override;
+    public
+      function GetEntityKey(constref AItem: TAccount) : Variant; override;
+      function GetItemField(constref AItem: TAccount; const ABindingName : AnsiString) : Variant; override;
+      procedure DehydrateItem(constref AItem: TAccount; var ATableRow: Variant); override;
+  end;
+
   { TAccountsDataSource }
 
-  TAccountsDataSource = class(TCustomDataSource<TAccount>)
+  TAccountsDataSource = class(TAccountsDataSourceBase)
     public type
       TOverview = record
         TotalPASC : UInt64;
@@ -23,8 +35,6 @@ type
       FKeys : TSortedHashSet<TAccountKey>;
     protected
       FLastOverview : TOverview;
-      function GetItemDisposePolicy : TItemDisposePolicy; override;
-      function GetColumns : TTableColumns;  override;
       function GetFilterKeys : TArray<TAccountKey>;
       procedure SetFilterKeys (const AKeys : TArray<TAccountKey>);
     public
@@ -33,11 +43,7 @@ type
       property FilterKeys : TArray<TAccountKey> read GetFilterKeys write SetFilterKeys;
       constructor Create(AOwner: TComponent); override;
       destructor Destroy; override;
-      function GetSearchCapabilities: TSearchCapabilities; override;
-      function GetEntityKey(constref AItem: TAccount) : Variant; override;
       procedure FetchAll(const AContainer : TList<TAccount>); override;
-      function GetItemField(constref AItem: TAccount; const AColumnName : utf8string) : Variant; override;
-      procedure DehydrateItem(constref AItem: TAccount; var ATableRow: Variant); override;
   end;
 
   { TOperationsDataSourceBase }
@@ -48,16 +54,15 @@ type
       function GetTimeSpan : TTimeSpan;
       procedure SetTimeSpan(const ASpan : TTimeSpan);
     protected
-      function GetItemDisposePolicy : TItemDisposePolicy; override;
-      function GetColumns : TTableColumns;  override;
+      function GetItemDisposePolicy : TDisposePolicy; override;
+      function GetColumns : TDataColumns;  override;
     public
       constructor Create(AOwner: TComponent); override;
       property TimeSpan : TTimeSpan read GetTimeSpan write SetTimeSpan;
       property StartBlock : Cardinal read FStart write FStart;
       property EndBlock : Cardinal read FEnd write FEnd;
-      function GetSearchCapabilities: TSearchCapabilities; override;
       function GetEntityKey(constref AItem: TOperationResume) : Variant; override;
-      function GetItemField(constref AItem: TOperationResume; const AColumnName : utf8string) : Variant; override;
+      function GetItemField(constref AItem: TOperationResume; const ABindingName : AnsiString) : Variant; override;
       procedure DehydrateItem(constref AItem: TOperationResume; var ATableRow: Variant); override;
   end;
 
@@ -100,7 +105,76 @@ type
 implementation
 
 uses
-  math, UCore, UWallet, UUserInterface, UMemory, UTime;
+  math, UCore, UWallet, UUserInterface, UTime;
+
+{ TAccountsDataSourceBase }
+
+function TAccountsDataSourceBase.GetItemDisposePolicy : TDisposePolicy;
+begin
+  Result := idpNone;
+end;
+
+function TAccountsDataSourceBase.GetColumns : TDataColumns;
+begin
+  Result := TDataColumns.Create(
+    TDataColumn.From('Account'),
+    TDataColumn.From('Name'),
+    TDataColumn.From('Balance'),
+    TDataColumn.From('Key'),
+    TDataColumn.From('State'),
+    TDataColumn.From('Price'),
+    TDataColumn.From('LockedUntil')
+  );
+end;
+
+function TAccountsDataSourceBase.GetEntityKey(constref AItem: TAccount) : Variant;
+begin
+  Result := AItem.account;
+end;
+
+function TAccountsDataSourceBase.GetItemField(constref AItem: TAccount; const ABindingName : AnsiString) : Variant;
+var
+  index : Integer;
+begin
+   if ABindingName = 'Account' then
+     Result := AItem.account
+   else if ABindingName = 'Name' then
+     Result := AItem.name
+   else if ABindingName = 'Balance' then
+     Result := TAccountComp.FormatMoneyDecimal(AItem.Balance)
+{   else if ABindingName = 'Key' then begin
+     index := TWallet.Keys.AccountsKeyList.IndexOfAccountKey(AItem.accountInfo.accountKey);
+     if index>=0 then
+        Result := TWallet.Keys[index].Name
+     else
+         Result := TAccountComp.AccountPublicKeyExport(AItem.accountInfo.accountKey); }
+   else if ABindingName = 'Key' then
+     Result := TAccountComp.AccountPublicKeyExport(AItem.accountInfo.accountKey)
+   else if ABindingName = 'AccType' then
+     Result := AItem.account_type
+   else if ABindingName = 'State' then
+     Result := AItem.accountInfo.state
+   else if ABindingName = 'Price' then
+     Result := TAccountComp.FormatMoneyDecimal(AItem.accountInfo.price)
+   else if ABindingName = 'LockedUntil' then
+     Result := AItem.accountInfo.locked_until_block
+   else raise Exception.Create(Format('Field not found "%s"', [ABindingName]));
+end;
+
+procedure TAccountsDataSourceBase.DehydrateItem(constref AItem: TAccount; var ATableRow: Variant);
+//var
+//  index : Integer;
+begin
+  // 'Account', 'Name', 'Balance', 'Key', 'AccType', 'State', 'Price', 'LockedUntil'
+  ATableRow.Account := TAccountComp.AccountNumberToAccountTxtNumber(AItem.account);
+  ATableRow.Name := Variant(AItem.name);
+  ATableRow.Balance := TAccountComp.FormatMoney(AItem.balance);
+  ATableRow.Key := TAccountComp.AccountPublicKeyExport(AItem.accountInfo.accountKey);
+  ATableRow.AccType := Word(AItem.account_type);
+  ATableRow.State := Cardinal(AItem.accountInfo.state);
+  ATableRow.Price := TAccountComp.FormatMoney(Aitem.accountInfo.price);
+  ATableRow.LockedUntil := LongWord(AItem.accountInfo.locked_until_block);
+end;
 
 { TAccountsDataSource }
 
@@ -128,30 +202,6 @@ begin
     FKeys.Add(AKeys[i]);
 end;
 
-function TAccountsDataSource.GetItemDisposePolicy : TItemDisposePolicy;
-begin
-  Result := idpNone;
-end;
-
-function TAccountsDataSource.GetColumns : TTableColumns;
-begin
-  Result := TTableColumns.Create('Account', 'Name', 'Balance');
-end;
-
-function TAccountsDataSource.GetSearchCapabilities: TSearchCapabilities;
-begin
-  Result := TSearchCapabilities.Create(
-    TSearchCapability.From('Account', SORTABLE_NUMERIC_FILTER),
-    TSearchCapability.From('Name', SORTABLE_TEXT_FILTER),
-    TSearchCapability.From('Balance', SORTABLE_NUMERIC_FILTER)
-  );
-end;
-
-function TAccountsDataSource.GetEntityKey(constref AItem: TAccount) : Variant;
-begin
-  Result := AItem.account;
-end;
-
 procedure TAccountsDataSource.FetchAll(const AContainer : TList<TAccount>);
 var
   i,j : integer;
@@ -194,53 +244,6 @@ begin
   FLastKnownUserAccounts := AContainer.ToArray;
 end;
 
-function TAccountsDataSource.GetItemField(constref AItem: TAccount; const AColumnName : utf8string) : Variant;
-var
-  index : Integer;
-begin
-   if AColumnName = 'Account' then
-     Result := AItem.account
-   else if AColumnName = 'Name' then
-     Result := AItem.name
-   else if AColumnName = 'Balance' then
-     Result := TAccountComp.FormatMoneyDecimal(AItem.Balance)
-   else if AColumnName = 'Key' then begin
-     index := TWallet.Keys.AccountsKeyList.IndexOfAccountKey(AItem.accountInfo.accountKey);
-     if index>=0 then
-        Result := TWallet.Keys[index].Name
-     else
-         Result := TAccountComp.AccountPublicKeyExport(AItem.accountInfo.accountKey);
-   end else if AColumnName = 'AccType' then
-     Result := AItem.account_type
-   else if AColumnName = 'State' then
-     Result := AItem.accountInfo.state
-   else if AColumnName = 'Price' then
-     Result := AItem.accountInfo.price
-   else if AColumnName = 'LockedUntil' then
-     Result := AItem.accountInfo.locked_until_block
-   else raise Exception.Create(Format('Field not found [%s]', [AColumnName]));
-end;
-
-procedure TAccountsDataSource.DehydrateItem(constref AItem: TAccount; var ATableRow: Variant);
-//var
-//  index : Integer;
-begin
-  // 'Account', 'Name', 'Balance', 'Key', 'AccType', 'State', 'Price', 'LockedUntil'
-  ATableRow.Account := TAccountComp.AccountNumberToAccountTxtNumber(AItem.account);
-  ATableRow.Name := Variant(AItem.name);
-  ATableRow.Balance := TAccountComp.FormatMoney(AItem.balance);
- // index := TWallet.Keys.AccountsKeyList.IndexOfAccountKey(AItem.accountInfo.accountKey);
- // if index>=0 then begin
- //   ATableRow.Key := TDataSourceTool.AccountKeyShortText(TWallet.Keys[index].Name)
- // end else begin
- //   ATableRow.Key := TDataSourceTool.AccountKeyShortText(TAccountComp.AccountPublicKeyExport(AItem.accountInfo.accountKey));
- // end;
-  ATableRow.AccType := Word(AItem.account_type);
-  ATableRow.State := Cardinal(AItem.accountInfo.state);
-  ATableRow.Price := TAccountComp.FormatMoney(Aitem.accountInfo.price);
-  //ATableRow.LockedUntil := Cardinal(AItem.accountInfo.locked_until_block);
-end;
-
 { TOperationsDataSourceBase }
 
 constructor TOperationsDataSourceBase.Create(AOwner:TComponent);
@@ -275,29 +278,24 @@ begin
  //XXXXXXXXXX TTimeSpan use not available at TPCOperationsComp  FStart := ClipValue(FEnd - (TPCOperationsComp.ConvertTimeSpanToBlockCount(ASpan) + 1), 0, FEnd);
 end;
 
-function TOperationsDataSourceBase.GetItemDisposePolicy : TItemDisposePolicy;
+function TOperationsDataSourceBase.GetItemDisposePolicy : TDisposePolicy;
 begin
   Result := idpNone;
 end;
 
-function TOperationsDataSourceBase.GetColumns : TTableColumns;
+function TOperationsDataSourceBase.GetColumns : TDataColumns;
 begin
-  Result := TTableColumns.Create('Time', 'Block', 'Account', 'Type', 'Amount', 'Fee', 'Balance', 'Payload', 'OPHASH', 'Description');
-end;
-
-function TOperationsDataSourceBase.GetSearchCapabilities: TSearchCapabilities;
-begin
-  Result := TSearchCapabilities.Create(
-    TSearchCapability.From('Time', SORTABLE_NUMERIC_FILTER),
-    TSearchCapability.From('Block', SORTABLE_TEXT_FILTER),
-    TSearchCapability.From('Account', SORTABLE_NUMERIC_FILTER),
-    TSearchCapability.From('Type', SORTABLE_NUMERIC_FILTER),
-    TSearchCapability.From('Amount', SORTABLE_NUMERIC_FILTER),
-    TSearchCapability.From('Fee', SORTABLE_NUMERIC_FILTER),
-    TSearchCapability.From('Balance', SORTABLE_NUMERIC_FILTER),
-    TSearchCapability.From('Payload', SORTABLE_TEXT_FILTER),
-    TSearchCapability.From('OPHASH', SORTABLE_TEXT_FILTER),
-    TSearchCapability.From('Description', SORTABLE_TEXT_FILTER)
+  Result := TDataColumns.Create(
+    TDataColumn.From('Time'),
+    TDataColumn.From('Block'),
+    TDataColumn.From('Account'),
+    TDataColumn.From('Type'),
+    TDataColumn.From('Amount'),
+    TDataColumn.From('Fee'),
+    TDataColumn.From('Balance'),
+    TDataColumn.From('Payload'),
+    TDataColumn.From('OPHASH'),
+    TDataColumn.From('Description')
   );
 end;
 
@@ -309,31 +307,31 @@ begin
     Result := nil;
 end;
 
-function TOperationsDataSourceBase.GetItemField(constref AItem: TOperationResume; const AColumnName : utf8string) : Variant;
+function TOperationsDataSourceBase.GetItemField(constref AItem: TOperationResume; const ABindingName : AnsiString) : Variant;
 var
   index : Integer;
 begin
-   if AColumnName = 'Time' then
+   if ABindingName = 'Time' then
      Result := AItem.Time
-   else if AColumnName = 'Block' then
+   else if ABindingName = 'Block' then
      Result := UInt64(AItem.Block) * 4294967296 + UInt32(AItem.NOpInsideBlock)   // number pattern = [block][opindex]
-   else if AColumnName = 'Account' then
+   else if ABindingName = 'Account' then
      Result := AItem.AffectedAccount
-   else if AColumnName = 'Type' then
+   else if ABindingName = 'Type' then
      Result := AItem.OpSubtype
-   else if AColumnName = 'Amount' then
+   else if ABindingName = 'Amount' then
      Result := TAccountComp.FormatMoneyDecimal(AItem.Amount)
-   else if AColumnName = 'Fee' then
+   else if ABindingName = 'Fee' then
      Result := TAccountComp.FormatMoneyDecimal(AItem.Fee)
-   else if AColumnName = 'Balance' then
+   else if ABindingName = 'Balance' then
      Result := TAccountComp.FormatMoneyDecimal(AItem.Balance)
-   else if AColumnName = 'Payload' then
+   else if ABindingName = 'Payload' then
      Result := AItem.PrintablePayload
-   else if AColumnName = 'OPHASH' then
+   else if ABindingName = 'OPHASH' then
      Result := TPCOperation.OperationHashAsHexa(AItem.OperationHash)
-   else if AColumnName = 'Description' then
+   else if ABindingName = 'Description' then
      Result :=  AItem.OperationTxt
-   else raise Exception.Create(Format('Field not found [%s]', [AColumnName]));
+   else raise Exception.Create(Format('Field not found [%s]', [ABindingName]));
 end;
 
 procedure TOperationsDataSourceBase.DehydrateItem(constref AItem: TOperationResume; var ATableRow: Variant);
@@ -357,11 +355,7 @@ begin
   ATableRow.&Type := Variant(TDataSourceTool.OperationShortText(AItem.OpType, AItem.OpSubtype));
 
   // Amount
-  ATableRow.Amount := TAccountComp.FormatMoney(AItem.Amount);
-  {if opr.Amount>0 then DrawGrid.Canvas.Font.Color := ClGreen
-  else if opr.Amount=0 then DrawGrid.Canvas.Font.Color := clGrayText
-  else DrawGrid.Canvas.Font.Color := clRed;
-  Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);}
+  ATableRow.Amount := AItem.Amount;
 
   // Fee
   ATableRow.Fee := TAccountComp.FormatMoney(AItem.Fee);
@@ -403,7 +397,6 @@ begin
 
 end;
 
-
 { TAccountsOperationsDataSource }
 
 constructor TAccountsOperationsDataSource.Create(AOwner:TComponent);

+ 1 - 0
src/gui/UCTRLWallet.lfm

@@ -10,6 +10,7 @@ object CTRLWallet: TCTRLWallet
   ClientWidth = 1151
   OnCreate = FormCreate
   OnResize = FormResize
+  LCLVersion = '1.8.2.0'
   Visible = False
   object PairSplitter1: TPairSplitter
     Left = 0

+ 69 - 27
src/gui/UCTRLWallet.pas

@@ -74,8 +74,8 @@ type
     procedure OnAccountsUpdated(Sender: TObject);
     procedure OnAccountsSelected(Sender: TObject; constref ASelection: TVisualGridSelection);
     procedure OnOperationSelected(Sender: TObject; constref ASelection: TVisualGridSelection);
-    procedure OnAccountsGridColumnInitialize(Sender: TObject; AColIndex: integer; AColumn: TVisualColumn);
-    procedure OnOperationsGridColumnInitialize(Sender: TObject; AColIndex: integer; AColumn: TVisualColumn);
+    procedure OnAccountsGridColumnInitialize(Sender: TObject; AColumn: TVisualColumn);
+    procedure OnOperationsGridColumnInitialize(Sender: TObject; AColumn: TVisualColumn);
     procedure OnPrepareAccountPopupMenu(Sender: TObject; constref ASelection: TVisualGridSelection; out APopupMenu: TPopupMenu);
     procedure OnPrepareOperationsPopupMenu(Sender: TObject; constref ASelection: TVisualGridSelection; out APopupMenu: TPopupMenu);
   public
@@ -87,8 +87,8 @@ type
 implementation
 
 uses
-  UUserInterface, UBlockChain, UWallet, UCrypto, UCore,
-  UCommon, UMemory, Generics.Defaults, UCommon.Collections;
+  UUserInterface, UCellRenderers, UBlockChain, UWallet, UCrypto, UCore,
+  UCommon, UMemory, Generics.Defaults, UCommon.Data, UCommon.Collections;
 
 {$R *.lfm}
 
@@ -118,11 +118,20 @@ begin
   FAccountsGrid.SelectionType := stMultiRow;
   FAccountsGrid.DeselectionType := dtDefault;
   FAccountsGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone, vgoAutoHidePaging];
-  FAccountsGrid.DefaultColumnWidths :=
-    TArray<integer>.Create(100,                   // Account
-    CT_VISUALGRID_STRETCH, // Name
-    100                    // Balance
-    );
+  with FAccountsGrid.AddColumn('Account') do begin
+    Width := 100;
+    Renderer := TCellRenderers.AccountNo;
+  end;
+  with FAccountsGrid.AddColumn('Name') do begin
+    StretchedToFill := true;
+    Renderer := TCellRenderers.AccountName;
+  end;
+  with FAccountsGrid.AddColumn('Balance') do begin
+    Width := 100;
+    StretchedToFill := true;
+    Renderer := TCellRenderers.PASCBalance;
+  end;
+
   FAccountsGrid.OnColumnInitialize := OnAccountsGridColumnInitialize;
   FAccountsGrid.OnSelection := OnAccountsSelected;
   FAccountsGrid.OnFinishedUpdating := OnAccountsUpdated;
@@ -135,18 +144,53 @@ begin
   FOperationsGrid.SelectionType := stRow;
   FOperationsGrid.DeselectionType := dtDefault;
   FOperationsGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone, vgoAutoHidePaging];
-  FOperationsGrid.DefaultColumnWidths :=
-    TArray<integer>.Create(130,                   // Time
-    CT_VISUALGRID_DEFAULT, // Block
-    100,                   // Account
-    150,                   // Type
-    130,                   // Amount
-    CT_VISUALGRID_DEFAULT, // Fee
-    100,                   // Balance
-    CT_VISUALGRID_DEFAULT, // Payload
-    80,                    // OPHASH
-    CT_VISUALGRID_STRETCH  // Description (stretch)
-  );
+  with FOperationsGrid.AddColumn('Time') do begin
+    Width := 130;
+    Renderer := TCellRenderers.Date_YYYYMMDDHHMMSS;
+    Filters := SORTABLE_NUMERIC_FILTER;
+  end;
+  with FOperationsGrid.AddColumn('Block') do begin
+    AutoWidth := true;
+    Filters := SORTABLE_TEXT_FILTER;
+  end;
+  with FOperationsGrid.AddColumn('Account') do begin
+    Width := 100;
+    Filters := SORTABLE_NUMERIC_FILTER;
+  end;
+  with FOperationsGrid.AddColumn('Type') do begin
+    Width := 150;
+    Filters := SORTABLE_NUMERIC_FILTER;
+  end;
+  with FOperationsGrid.AddColumn('Amount') do begin
+    Width := 150;
+    Renderer := TCellRenderers.PASCTransfer;
+    Filters := SORTABLE_NUMERIC_FILTER;
+  end;
+  with FOperationsGrid.AddColumn('Fee') do begin
+    AutoWidth := true;
+    Renderer := TCellRenderers.PASCTransfer;
+    Filters := SORTABLE_NUMERIC_FILTER;
+  end;
+  with FOperationsGrid.AddColumn('Balance') do begin
+    Width := 100;
+    Renderer := TCellRenderers.PASCBalance;
+    Filters := SORTABLE_NUMERIC_FILTER;
+  end;
+  with FOperationsGrid.AddColumn('Payload') do begin
+    AutoWidth := true;
+    Renderer := TCellRenderers.Payload;
+    Filters := SORTABLE_TEXT_FILTER;
+  end;
+  with FOperationsGrid.AddColumn('OPHASH') do begin
+    Width := 80;
+    Renderer := TCellRenderers.OPHASH;
+    Filters := SORTABLE_TEXT_FILTER;
+  end;
+  with FOperationsGrid.AddColumn('Description') do begin
+    StretchedToFill := true;
+    Renderer := TCellRenderers.SmallText;
+    Filters := SORTABLE_TEXT_FILTER;
+  end;
   FOperationsGrid.OnColumnInitialize := OnOperationsGridColumnInitialize;
   FOperationsGrid.OnSelection := OnOperationSelected;
   FOperationsGrid.OnPreparePopupMenu := OnPrepareOperationsPopupMenu;
@@ -259,18 +303,16 @@ begin
   end;
 end;
 
-procedure TCTRLWallet.OnAccountsGridColumnInitialize(Sender: TObject;
-  AColIndex: integer; AColumn: TVisualColumn);
+procedure TCTRLWallet.OnAccountsGridColumnInitialize(Sender: TObject; AColumn: TVisualColumn);
 begin
-  case AColIndex of
+  case AColumn.Index of
     2: AColumn.InternalColumn.Alignment := taRightJustify;
   end;
 end;
 
-procedure TCTRLWallet.OnOperationsGridColumnInitialize(Sender: TObject;
-  AColIndex: integer; AColumn: TVisualColumn);
+procedure TCTRLWallet.OnOperationsGridColumnInitialize(Sender: TObject; AColumn: TVisualColumn);
 begin
-  case AColIndex of
+  case AColumn.Index of
     4, 5, 6: AColumn.InternalColumn.Alignment := taRightJustify;
   end;
 end;

+ 109 - 0
src/gui/UCellRenderers.pas

@@ -0,0 +1,109 @@
+unit UCellRenderers;
+
+{$mode delphi}
+
+{ Copyright (c) 2018 by Herman Schoenfeld
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+}
+
+interface
+
+uses
+   Classes, Forms, Controls, Graphics, Dialogs, ExtCtrls, Grids,
+   UVisualGrid, variants;
+
+type
+
+  { TCellRenderers }
+
+  TCellRenderers = class
+    class procedure AccountNo(Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+    class procedure AccountName(Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+    class procedure Date_YYYYMMDDHHMMSS(Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+    class procedure PASCTransfer(Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+    class procedure PASCBalance(Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+    class procedure Payload(Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+    class procedure OPHASH(Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+    class procedure SmallText(Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+  end;
+
+implementation
+
+uses
+  SysUtils, UCommon, UAccounts;
+
+const
+  CT_PASCBALANCE_POS_COL = clGreen;
+  CT_PASCBALANCE_NEU_COL = clGrayText;
+  CT_PASCBALANCE_NEG_COL = clRed;
+
+{ TCellRenderers }
+
+class procedure TCellRenderers.AccountNo (Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+var
+ LTextStyle: TTextStyle;
+begin
+ LTextStyle := Canvas.TextStyle;
+ LTextStyle.Alignment:=taLeftJustify;
+ Canvas.TextStyle:=LTextStyle;
+ Canvas.TextRect(Rect, Rect.Left, Rect.Top, CellData, LTextStyle);
+ Handled := true;
+end;
+
+class procedure TCellRenderers.AccountName (Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+var
+ LTextStyle: TTextStyle;
+begin
+ LTextStyle := Canvas.TextStyle;
+ LTextStyle.Alignment:=taLeftJustify;
+ Canvas.TextStyle:=LTextStyle;
+ Canvas.Font.Bold := true;
+ Canvas.TextRect(Rect, Rect.Left, Rect.Top, CellData, LTextStyle);
+ Handled := true;
+end;
+
+class procedure TCellRenderers.Date_YYYYMMDDHHMMSS (Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+begin
+  Handled := False;
+end;
+
+class procedure TCellRenderers.PASCTransfer (Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+begin
+  Handled := False;
+end;
+
+class procedure TCellRenderers.PASCBalance (Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+var
+  LNum : Int64;
+  LTextStyle: TTextStyle;
+begin
+  if NOT VarIsNumeric(CellData) then
+    exit;
+  LNum := CellData;
+  Canvas.Font.Color:= IIF (LNum < 0, CT_PASCBALANCE_NEG_COL, IIF(LNum > 0, CT_PASCBALANCE_POS_COL, CT_PASCBALANCE_NEU_COL));
+  Canvas.Font.Style:=[fsBold];
+  LTextStyle := Canvas.TextStyle;
+  LTextStyle.Alignment:=taRightJustify;
+  Canvas.TextRect(Rect, Rect.Left, Rect.Top, TAccountComp.FormatMoney(LNum), LTextStyle);
+  Handled := true;
+end;
+
+class procedure TCellRenderers.Payload (Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+begin
+  Handled := False;
+end;
+
+class procedure TCellRenderers.OPHASH (Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+begin
+  Handled := False;
+end;
+
+class procedure TCellRenderers.SmallText (Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
+begin
+ Handled := False;
+end;
+
+end.
+

+ 0 - 2
src/gui/UUserInterface.pas

@@ -471,7 +471,6 @@ begin
   ShowMemoText(parentForm, Format('Account/Operation: %s/%s', [account.GetAccountString, operation.GetPrintableOPHASH]), text);
 end;
 
-
 // TODO - refactor with accounts as ARRAY
 class procedure TUserInterface.ShowNewOperationDialog(parentForm : TForm; accounts : TOrderedCardinalList; defaultFee : Cardinal);
 begin
@@ -775,7 +774,6 @@ end;
 
 {%endregion}
 
-
 {%region Auxillary methods}
 
 class procedure TUserInterface.SetMainFormMode(AMode: TFRMMainFormMode);

+ 1 - 0
src/gui/wizards/UWIZSendPASC_Confirmation.lfm

@@ -6,6 +6,7 @@ object WIZSendPASC_Confirmation: TWIZSendPASC_Confirmation
   Caption = 'WIZSendPASC_Confirmation'
   ClientHeight = 253
   ClientWidth = 429
+  LCLVersion = '1.8.2.0'
   Visible = False
   object GroupBox1: TGroupBox
     Left = 10

+ 35 - 35
src/gui/wizards/UWIZSendPASC_Confirmation.pas

@@ -37,23 +37,21 @@ implementation
 
 {$R *.lfm}
 
-uses UAccounts, UCommon, UCommon.UI, Generics.Collections;
+uses UAccounts, UDataSources, UCommon, UCommon.UI, Generics.Collections;
 
 type
 
   { TAccountSenderDataSource }
 
-  TAccountSenderDataSource = class(TCustomDataSource<TAccount>)
+  TAccountSenderDataSource = class(TAccountsDataSourceBase)
     private
       FModel : TWIZSendPASCModel;
     protected
-      function GetColumns : TTableColumns;  override;
+      function GetColumns : TDataColumns;
     public
       property Model : TWIZSendPASCModel read FModel write FModel;
-      function GetSearchCapabilities: TSearchCapabilities; override;
-      function GetEntityKey(constref AItem: TAccount) : Variant; override;
       procedure FetchAll(const AContainer : TList<TAccount>); override;
-      function GetItemField(constref AItem: TAccount; const AColumnName : utf8string) : Variant; override;
+      function GetItemField(constref AItem: TAccount; const ABindingName : AnsiString) : Variant; override;
       procedure DehydrateItem(constref AItem: TAccount; var ATableRow: Variant); override;
   end;
 
@@ -70,13 +68,25 @@ begin
   FSendersGrid.AutoPageSize := True;
   FSendersGrid.SelectionType := stNone;
   FSendersGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone, vgoAutoHidePaging];
-  FSendersGrid.DefaultColumnWidths :=
-    TArray<integer>.Create(
-    100, // Sender Account
-    100, // Balance
-    100, // AmountToSend
-    100 // Fee
-   );
+  with FSendersGrid.AddColumn('Sender') do begin
+    Binding := 'SenderAccount';
+    Filters := SORTABLE_NUMERIC_FILTER;
+    Width := 100;
+  end;
+  with FSendersGrid.AddColumn('Balance') do begin
+    Filters := SORTABLE_NUMERIC_FILTER;
+    Width := 100;
+  end;
+  with FSendersGrid.AddColumn('Amount To Send') do begin
+    Binding := 'AmountToSend';
+    Filters := SORTABLE_TEXT_FILTER;
+    Width := 100;
+  end;
+  with FSendersGrid.AddColumn('Fee') do begin
+    Filters := SORTABLE_TEXT_FILTER;
+    Width := 100;
+  end;
+
    data := TAccountSenderDataSource.Create(FSendersGrid);
    data.Model := Model;
    FSendersGrid.DataSource := data;
@@ -87,39 +97,29 @@ end;
 
 { TAccountSenderDataSource }
 
-function TAccountSenderDataSource.GetColumns : TTableColumns;
+function TAccountSenderDataSource.GetColumns : TDataColumns;
 begin
-  Result := TTableColumns.Create('SenderAccount', 'Balance', 'AmountToSend', 'Fee');
-end;
-
-function TAccountSenderDataSource.GetSearchCapabilities: TSearchCapabilities;
-begin
-  Result := TSearchCapabilities.Create(
-    TSearchCapability.From('SenderAccount', SORTABLE_NUMERIC_FILTER),
-    TSearchCapability.From('Balance', SORTABLE_TEXT_FILTER),
-    TSearchCapability.From('AmountToSend', SORTABLE_TEXT_FILTER),
-    TSearchCapability.From('Fee', SORTABLE_TEXT_FILTER)
+  Result := TDataColumns.Create(
+    TDataColumn.From('SenderAccount'),
+    TDataColumn.From('Balance'),
+    TDataColumn.From('AmountToSend'),
+    TDataColumn.From('Fee')
   );
 end;
 
-function TAccountSenderDataSource.GetEntityKey(constref AItem: TAccount) : Variant;
-begin
-  Result := AItem.account;
-end;
-
-function TAccountSenderDataSource.GetItemField(constref AItem: TAccount; const AColumnName : utf8string) : Variant;
+function TAccountSenderDataSource.GetItemField(constref AItem: TAccount; const ABindingName : AnsiString) : Variant;
 var
   index : Integer;
 begin
-   if AColumnName = 'SenderAccount' then
+   if ABindingName = 'SenderAccount' then
      Result := AItem.account
-   else if AColumnName = 'Balance' then
+   else if ABindingName = 'Balance' then
      Result := TAccountComp.FormatMoneyDecimal(AItem.Balance)
-   else if AColumnName = 'AmountToSend' then
+   else if ABindingName = 'AmountToSend' then
      Result := Model.AmountToSend
-     else if AColumnName = 'Fee' then
+     else if ABindingName = 'Fee' then
      Result := TAccountComp.FormatMoney(Model.DefaultFee)
-   else raise Exception.Create(Format('Field not found [%s]', [AColumnName]));
+   else raise Exception.Create(Format('Field not found [%s]', [ABindingName]));
 end;
 
 procedure TAccountSenderDataSource.DehydrateItem(constref AItem: TAccount; var ATableRow: Variant);

+ 1 - 1
src/gui/wizards/UWIZSendPASC_Start.lfm

@@ -6,7 +6,7 @@ object WIZSendPASC_Start: TWIZSendPASC_Start
   Caption = 'WIZSendPASC_Start'
   ClientHeight = 261
   ClientWidth = 429
-  LCLVersion = '1.8.1.0'
+  LCLVersion = '1.8.2.0'
   Visible = False
   object gpSender: TGroupBox
     Left = 4

+ 15 - 52
src/gui/wizards/UWIZSendPASC_Start.pas

@@ -39,24 +39,18 @@ implementation
 
 {$R *.lfm}
 
-uses UAccounts, UCommon, UCommon.UI, Generics.Collections;
+uses UAccounts, UDataSources, UCommon, UCommon.UI, Generics.Collections;
 
 type
 
   { TAccountSenderDataSource }
 
-  TAccountSenderDataSource = class(TCustomDataSource<TAccount>)
+  TAccountSenderDataSource = class(TAccountsDataSourceBase)
     private
       FModel : TWIZSendPASCModel;
-    protected
-      function GetColumns : TTableColumns;  override;
     public
       property Model : TWIZSendPASCModel read FModel write FModel;
-      function GetSearchCapabilities: TSearchCapabilities; override;
-      function GetEntityKey(constref AItem: TAccount) : Variant; override;
       procedure FetchAll(const AContainer : TList<TAccount>); override;
-      function GetItemField(constref AItem: TAccount; const AColumnName : utf8string) : Variant; override;
-      procedure DehydrateItem(constref AItem: TAccount; var ATableRow: Variant); override;
   end;
 
 { TWIZSendPASC_Start }
@@ -72,15 +66,19 @@ begin
   FSendersGrid.AutoPageSize := True;
   FSendersGrid.SelectionType := stNone;
   FSendersGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone, vgoAutoHidePaging];
-  FSendersGrid.DefaultColumnWidths :=
-    TArray<integer>.Create(
-    CT_VISUALGRID_STRETCH, // Account
-    100                    // Amount
-   );
-   data := TAccountSenderDataSource.Create(FSendersGrid);
-   data.Model := Model;
-   FSendersGrid.DataSource := data;
-   paGrid.AddControlDockCenter(FSendersGrid);
+  with FSendersGrid.AddColumn('Account') do begin
+    StretchedToFill := true;
+    Filters := SORTABLE_NUMERIC_FILTER;
+  end;
+  with FSendersGrid.AddColumn('Amount') do begin
+    Binding := 'Balance';
+    Width := 100;
+    Filters := SORTABLE_NUMERIC_FILTER;
+  end;
+  data := TAccountSenderDataSource.Create(FSendersGrid);
+  data.Model := Model;
+  FSendersGrid.DataSource := data;
+  paGrid.AddControlDockCenter(FSendersGrid);
 end;
 
 procedure TWIZSendPASC_Start.OnNext;
@@ -90,41 +88,6 @@ end;
 
 { TAccountSenderDataSource }
 
-function TAccountSenderDataSource.GetColumns : TTableColumns;
-begin
-  Result := TTableColumns.Create('Account', 'Amount');
-end;
-
-function TAccountSenderDataSource.GetSearchCapabilities: TSearchCapabilities;
-begin
-  Result := TSearchCapabilities.Create(
-    TSearchCapability.From('Account', SORTABLE_NUMERIC_FILTER),
-    TSearchCapability.From('Amount', SORTABLE_TEXT_FILTER)
-  );
-end;
-
-function TAccountSenderDataSource.GetEntityKey(constref AItem: TAccount) : Variant;
-begin
-  Result := AItem.account;
-end;
-
-function TAccountSenderDataSource.GetItemField(constref AItem: TAccount; const AColumnName : utf8string) : Variant;
-var
-  index : Integer;
-begin
-   if AColumnName = 'Account' then
-     Result := AItem.account
-   else if AColumnName = 'Amount' then
-     Result := TAccountComp.FormatMoneyDecimal(AItem.Balance)
-   else raise Exception.Create(Format('Field not found [%s]', [AColumnName]));
-end;
-
-procedure TAccountSenderDataSource.DehydrateItem(constref AItem: TAccount; var ATableRow: Variant);
-begin
-  ATableRow.Account := TAccountComp.AccountNumberToAccountTxtNumber(AItem.account);
-  ATableRow.Amount := TAccountComp.FormatMoney(AItem.balance);
-end;
-
 procedure TAccountSenderDataSource.FetchAll(const AContainer : TList<TAccount>);
 var
   i: Integer;

+ 1 - 1
src/gui/wizards/UWIZSendPASC_Transaction.lfm

@@ -7,7 +7,7 @@ object WIZSendPASC_Transaction: TWIZSendPASC_Transaction
   Caption = 'WIZSendPASC_Transaction'
   ClientHeight = 253
   ClientWidth = 429
-  LCLVersion = '1.8.1.0'
+  LCLVersion = '1.8.2.0'
   Visible = False
   object gbTransaction: TGroupBox
     Left = 22

+ 12 - 14
src/gui/wizards/UWIZSendPASC_Transaction.pas

@@ -14,8 +14,7 @@ interface
 uses
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
   ExtCtrls, Buttons, UCommon, UCommon.Collections, UWallet,
-  UFRMAccountSelect, UNode, UWizard, UWIZSendPASC, UWIZSendPASC_TransactionPayload,
-  UWIZSendPASC_Confirmation;
+  UFRMAccountSelect, UNode, UWizard, UWIZSendPASC, UWIZSendPASC_Confirmation;
 
 type
 
@@ -37,7 +36,6 @@ type
     procedure btnSearchClick(Sender: TObject);
     procedure cbSignerAccountChange(Sender: TObject);
 
-
   public
     procedure OnPresent; override;
     procedure OnNext; override;
@@ -177,8 +175,6 @@ begin
     cbSignerAccount.Items.Objects[cbSignerAccount.ItemIndex])];
   Model.DestinationAccount := GetAccounts(GetAccNoWithoutChecksum(edtDestAcc.Text));
   Model.AmountToSend := edtAmt.Text;
-  UpdatePath(ptReplaceAllNext, [TWIZSendPASC_TransactionPayload,
-    TWIZSendPASC_Confirmation]);
 end;
 
 function TWIZSendPASC_Transaction.Validate(out message: ansistring): boolean;
@@ -192,6 +188,7 @@ var
   AccountNumbersWithChecksum: TArray<string>;
   Accounts: TArray<TAccount>;
   c: cardinal;
+  DestAccount: TAccount;
   amount, opfee: int64;
   i: integer;
 begin
@@ -219,6 +216,7 @@ begin
     Exit;
   end;
 
+  DestAccount := TNode.Node.Operations.SafeBoxTransaction.account(c);
   if Length(Accounts) = 1 then
   begin
     if not TAccountComp.TxtToMoney(edtAmt.Text, amount) then
@@ -246,15 +244,15 @@ begin
     Exit;
   end;
 
-  if Length(Accounts) = 1 then
-  begin
-    if (Accounts[0].balance < (amount + Model.DefaultFee)) then
-    begin
-      message := 'Insufficient funds';
-      Result := False;
-      Exit;
-    end;
-  end;
+  //for i := Low(Accounts) to High(Accounts) do
+  //begin
+  //  if (Accounts[i].balance < (amount + opfee)) then
+  //  begin
+  //    message := 'Insufficient funds';
+  //    Result := False;
+  //    Exit;
+  //  end;
+  //end;
 
 end;
 

+ 7 - 7
src/libraries/sphere10/UCommon.Collections.pas

@@ -20,7 +20,7 @@ unit UCommon.Collections;
 interface
 
 uses
-  Classes, SysUtils, Generics.Collections, Generics.Defaults, UCommon;
+  Classes, SysUtils, Generics.Collections, Generics.Defaults,UMemory, UCommon;
 
 type
 
@@ -110,10 +110,10 @@ type
     class function Skip(const AList: TList<T>; const ACount : SizeInt) : SizeInt;
     class function Take(const AList: TList<T>; const ACount : SizeInt) : SizeInt;
     class function RemoveBy(const AList: TList<T>; const APredicate: IPredicate<T>) : SizeInt; overload;
-    class function RemoveBy(const AList: TList<T>; const APredicate: IPredicate<T>; const ADisposePolicy : TItemDisposePolicy) : SizeInt; overload;
+    class function RemoveBy(const AList: TList<T>; const APredicate: IPredicate<T>; const ADisposePolicy : TDisposePolicy) : SizeInt; overload;
     class function FilterBy(const AList: TList<T>; const APredicate: IPredicate<T>) : SizeInt; overload;
-    class function FilterBy(const AList: TList<T>; const APredicate: IPredicate<T>; const ADisposePolicy : TItemDisposePolicy) : SizeInt; overload;
-    class procedure DiposeItem(const AList: TList<T>; const index : SizeInt; const ADisposePolicy : TItemDisposePolicy);
+    class function FilterBy(const AList: TList<T>; const APredicate: IPredicate<T>; const ADisposePolicy : TDisposePolicy) : SizeInt; overload;
+    class procedure DiposeItem(const AList: TList<T>; const index : SizeInt; const ADisposePolicy : TDisposePolicy);
   end;
 
   TListTool<T1, T2> = class
@@ -723,7 +723,7 @@ begin
   Result := RemoveBy(AList, APredicate, idpNone);
 end;
 
-class function TListTool<T>.RemoveBy(const AList: TList<T>; const APredicate: IPredicate<T>; const ADisposePolicy : TItemDisposePolicy) : SizeInt;
+class function TListTool<T>.RemoveBy(const AList: TList<T>; const APredicate: IPredicate<T>; const ADisposePolicy : TDisposePolicy) : SizeInt;
 var
   i : SizeInt;
   item : T;
@@ -746,12 +746,12 @@ begin
   Result := FilterBy(AList, APredicate, idpNone);
 end;
 
-class function TListTool<T>.FilterBy(const AList: TList<T>; const APredicate: IPredicate<T>; const ADisposePolicy : TItemDisposePolicy) : SizeInt;
+class function TListTool<T>.FilterBy(const AList: TList<T>; const APredicate: IPredicate<T>; const ADisposePolicy : TDisposePolicy) : SizeInt;
 begin
   Result := RemoveBy(AList, TPredicateTool<T>.NegatePredicate ( APredicate ) );
 end;
 
-class procedure TListTool<T>.DiposeItem(const AList: TList<T>; const index : SizeInt; const ADisposePolicy : TItemDisposePolicy);
+class procedure TListTool<T>.DiposeItem(const AList: TList<T>; const index : SizeInt; const ADisposePolicy : TDisposePolicy);
 var
   item : T;
 begin

+ 85 - 112
src/libraries/sphere10/UCommon.Data.pas

@@ -22,75 +22,71 @@ unit UCommon.Data;
 interface
 
 uses
-  Classes, SysUtils, Generics.Collections, UCommon, UCommon.Collections, Generics.Defaults,
+  Classes, SysUtils, Generics.Collections, UMemory, UCommon, UCommon.Collections, Generics.Defaults,
   Variants, LazUTF8, math, typinfo, syncobjs;
 
 type
   TSortDirection = (sdNone, sdAscending, sdDescending);
   TFilterOperand = (foAnd, foOr);
   TSortNullPolicy = (snpNone, snpNullFirst, snpNullLast);
-  TVisualGridFilter = (vgfMatchTextExact, vgfMatchTextBeginning, vgfMatchTextEnd,
-    vgfMatchTextAnywhere, vgfNumericEQ, vgfNumericLT, vgfNumericLTE, vgfNumericGT,
-    vgfNumericGTE, vgfNumericBetweenInclusive, vgfNumericBetweenExclusive, vgfSortable);
-  TVisualGridFilters = set of TVisualGridFilter;
+  TDataFilter = (vgfMatchTextExact, vgfMatchTextBeginning, vgfMatchTextEnd,
+     vgfMatchTextAnywhere, vgfNumericEQ, vgfNumericLT, vgfNumericLTE, vgfNumericGT,
+     vgfNumericGTE, vgfNumericBetweenInclusive, vgfNumericBetweenExclusive, vgfSortable);
+   TDataFilters = set of TDataFilter;
 
 const
-  TEXT_FILTER = [vgfMatchTextExact, vgfMatchTextBeginning, vgfMatchTextEnd, vgfMatchTextAnywhere];
-  NUMERIC_FILTER = [vgfNumericEQ, vgfNumericLT, vgfNumericLTE, vgfNumericGT, vgfNumericGTE, vgfNumericBetweenInclusive, vgfNumericBetweenExclusive];
-  SORTABLE_TEXT_FILTER = TEXT_FILTER + [vgfSortable];
-  SORTABLE_NUMERIC_FILTER = NUMERIC_FILTER + [vgfSortable];
-  SORTABLE_FILTER = [vgfSortable];
-  NON_SORTABLE_FILTER = [vgfMatchTextExact, vgfMatchTextBeginning, vgfMatchTextEnd,
-    vgfMatchTextAnywhere, vgfNumericEQ, vgfNumericLT, vgfNumericLTE, vgfNumericGT,
-    vgfNumericGTE, vgfNumericBetweenInclusive, vgfNumericBetweenExclusive];
-
-
-type
-
-  { TTableColumn }
-
-  //TODO: this structure is not being used
-  TTableColumn = record
-    Name : utf8string;
-    Binding : AnsiString;
-    class function From(AName: AnsiString) : TTableColumn; overload; static;
-    class function From(AName: utf8string; ABinding: AnsiString) : TTableColumn; overload; static;
+   TEXT_FILTER = [vgfMatchTextExact, vgfMatchTextBeginning, vgfMatchTextEnd, vgfMatchTextAnywhere];
+   NUMERIC_FILTER = [vgfNumericEQ, vgfNumericLT, vgfNumericLTE, vgfNumericGT, vgfNumericGTE, vgfNumericBetweenInclusive, vgfNumericBetweenExclusive];
+   SORTABLE_TEXT_FILTER = TEXT_FILTER + [vgfSortable];
+   SORTABLE_NUMERIC_FILTER = NUMERIC_FILTER + [vgfSortable];
+   SORTABLE_FILTER = [vgfSortable];
+   NON_SORTABLE_FILTER = [vgfMatchTextExact, vgfMatchTextBeginning, vgfMatchTextEnd,
+     vgfMatchTextAnywhere, vgfNumericEQ, vgfNumericLT, vgfNumericLTE, vgfNumericGT,
+     vgfNumericGTE, vgfNumericBetweenInclusive, vgfNumericBetweenExclusive];
+
+ type
+  { TDataColumn }
+
+  TDataColumn = record
+    Name : AnsiString;
+    // FType: typecode??
+    class function From(AName: AnsiString) : TDataColumn; overload; static;
   end;
 
-  TTableColumns = TArray<utf8string>;  //TODO: Maciej, should this be array of TTableColumn or just change header title in gui?
-  PTableColumns = ^TTableColumns;
-  ETableRow = class(Exception);
+  TDataColumns = TArray<TDataColumn>;
 
-  { TTableRow }
+  { TDataRow }
 
-  TTableRow = class(TInvokeableVariantType)
+  TDataRow = class(TInvokeableVariantType)
   private
     class constructor Create;
     class destructor Destroy;
   protected type
-    TColumnMapToIndex = TDictionary<utf8string, Integer>;
-    TColumnsDictionary = TObjectDictionary<PTableColumns, TColumnMapToIndex>;
+    TColumnMapToIndex = TDictionary<AnsiString, Integer>;
+    TColumnsDictionary = TObjectDictionary<AnsiString, TColumnMapToIndex>;
   protected class var
     FColumns: TColumnsDictionary;
   protected
-    class function MapColumns(AColumns: PTableColumns): TColumnMapToIndex;
+    class function MapColumns(const ADataSourceClassName : AnsiString; const AColumns: TDataColumns): TColumnMapToIndex;
   public
     function GetProperty(var Dest: TVarData; const V: TVarData; const Name: string): Boolean; override;
     function SetProperty(var V: TVarData; const Name: string; const Value: TVarData): Boolean; override;
     procedure Clear(var V: TVarData); override;
     procedure Copy(var Dest: TVarData; const Source: TVarData; const Indirect: Boolean); override;
     function DoFunction(var Dest: TVarData; const V: TVarData; const Name: string; const Arguments: TVarDataArray): Boolean; override;
-    class function New(AColumns: PTableColumns): Variant;
+    class function New(const ADataSourceClassName : AnsiString; const AColumns: TDataColumns): Variant;
   end;
 
-  TTableRowData = packed record
+  ETableRow = class(Exception);
+
+  TDataRowData = packed record
   public
     vtype: tvartype;
   private
     vfiller1 : word;
     vfiller2: int32;
   public
-    vcolumnmap: TTableRow.TColumnMapToIndex;
+    vcolumnmap: TDataRow.TColumnMapToIndex;
     vvalues: TArray<Variant>;
   end;
 
@@ -99,8 +95,9 @@ type
   TColumnFilter = record
     ColumnName: utf8string;
     Sort: TSortDirection;
-    Filter: TVisualGridFilter;
+    Filter: TDataFilter;
     Values: array of Variant;
+    SortSequence : Integer;
   end;
 
   { TFilterCriteria }
@@ -120,11 +117,10 @@ type
   PDataTable = ^TDataTable;
   TDataTable = record
   public
-    Columns: TTableColumns;
+    Columns: TDataColumns;
     Rows : TArray<Variant>;
   end;
 
-
   { TColumnFilterPredicate -- should be implementation only }
 
   TColumnFilterPredicate<T> = class (TInterfacedObject, IPredicate<T>)
@@ -167,49 +163,33 @@ type
     TotalDataCount: Integer;
   end;
 
-  { TSearchCapability }
-
-  PSearchCapability = ^TSearchCapability;
-  TSearchCapability = record
-    ColumnName : utf8string;
-    SupportedFilters : TVisualGridFilters;
-    class function From(const AName : utf8string; AFilterType : TVisualGridFilters) : TSearchCapability; static;
-  end;
-
-  { TSearchCapabilities }
-
-  TSearchCapabilities = array of TSearchCapability;
-
   { IDataSource }
 
   IDataSource = interface
     function FetchPage(constref AParams: TPageFetchParams; var ADataTable: TDataTable): TPageFetchResult;
-    function GetSearchCapabilities: TSearchCapabilities;
-    property SearchCapabilities : TSearchCapabilities read GetSearchCapabilities;
   end;
 
   { TCustomDataSource }
-// TODO: split  into TBindedDataSource <- TCustomDataSource
+
   TCustomDataSource<T> = class(TComponent, IDataSource)
     private
       FLock : TCriticalSection;
-      FColumns : TTableColumns;
+      FClassName : AnsiString;
     protected
       function GetNullPolicy(const AFilter : TColumnFilter) : TSortNullPolicy; virtual;
-      function GetItemDisposePolicy : TItemDisposePolicy; virtual; abstract;
-      function GetColumns : TTableColumns; virtual; abstract;
+      function GetItemDisposePolicy : TDisposePolicy; virtual; abstract;
+      function GetColumns : TDataColumns; virtual; abstract;
       function ApplyColumnSort(constref Left, Right : T; constref AFilter: TColumnFilter) : Integer; virtual;
       function ApplyColumnFilter(constref AItem: T; constref AFilter: TColumnFilter) : boolean; virtual;
-      function GetItemField(constref AItem: T; const AColumnName : utf8string) : Variant; virtual; abstract;
-      procedure DehydrateItem(constref AItem: T; var ATableRow: Variant); virtual; abstract;
+      function GetItemField(constref AItem: T; const ABindingName : AnsiString) : Variant; virtual; abstract;
+      procedure DehydrateItem(constref AItem: T; var ADataRow: Variant); virtual; abstract;
       function FetchPage(constref AParams: TPageFetchParams; var ADataTable: TDataTable): TPageFetchResult;
-      function GetSearchCapabilities: TSearchCapabilities; virtual; abstract;
       function GetEntityKey(constref AItem: T) : Variant; virtual;
       procedure OnBeforeFetchAll(constref AParams: TPageFetchParams); virtual;
       procedure FetchAll(const AContainer : TList<T>); virtual; abstract;
       procedure OnAfterFetchAll(constref AParams: TPageFetchParams); virtual;
     public
-      constructor Create(AOwner: TComponent); override;
+      constructor Create(AOwner: TComponent); override; overload;
       destructor Destroy; override;
   end;
 
@@ -272,63 +252,56 @@ resourcestring
 
 implementation
 
-uses dateutils, UMemory;
+uses dateutils;
 
 { VARIABLES }
 
 var
-  TableRowType: TTableRow = nil;
+  DataRowType: TDataRow = nil;
 
-{ TTableColumn }
+{ TDataColumn }
 
-class function TTableColumn.From(AName: AnsiString) : TTableColumn;
+class function TDataColumn.From(AName: AnsiString) : TDataColumn;
 begin
   Result.Name := AName;
-  Result.Binding := AName;
 end;
 
-class function TTableColumn.From(AName: utf8string; ABinding: AnsiString) : TTableColumn;
-begin
-  Result.Name := AName;
-  Result.Binding := ABinding;
-end;
+{ TDataRow }
 
-{ TTableRow }
-
-class constructor TTableRow.Create;
+class constructor TDataRow.Create;
 begin
   FColumns := TColumnsDictionary.Create([doOwnsValues]);
 end;
 
-class destructor TTableRow.Destroy;
+class destructor TDataRow.Destroy;
 begin
   FColumns.Free;
 end;
 
-class function TTableRow.MapColumns(AColumns: PTableColumns): TColumnMapToIndex;
+class function TDataRow.MapColumns(const ADataSourceClassName : AnsiString; const AColumns: TDataColumns): TColumnMapToIndex;
 var
   i: Integer;
 begin
   Result := TColumnMapToIndex.Create;
-  for i := 0 to High(AColumns^) do
-    Result.Add(AColumns^[i], i);
+  for i := Low(AColumns) to High(AColumns) do
+    Result.Add(AColumns[i].Name, i);
   Result.Add('__KEY', i + 1);
-  FColumns.Add(AColumns, Result);
+  FColumns.Add(ADataSourceClassName, Result);
 end;
 
-function TTableRow.GetProperty(var Dest: TVarData;
+function TDataRow.GetProperty(var Dest: TVarData;
   const V: TVarData; const Name: string): Boolean;
 var
-  LRow: TTableRowData absolute V;
+  LRow: TDataRowData absolute V;
 begin
   Variant(Dest) := LRow.vvalues[LRow.vcolumnmap[Name]];
   Result := true;
 end;
 
-function TTableRow.SetProperty(var V: TVarData; const Name: string;
+function TDataRow.SetProperty(var V: TVarData; const Name: string;
   const Value: TVarData): Boolean;
 var
-  LRow: TTableRowData absolute V;
+  LRow: TDataRowData absolute V;
 begin
   if NOT LRow.vcolumnmap.ContainsKey(Name) then
     Exit(true); // TODO: Re-enable this when TVisualColumn added -- ETableRow.Create(Format('TableRow did not have column "%s"', [Name]));
@@ -337,17 +310,17 @@ begin
   Result := true;
 end;
 
-procedure TTableRow.Clear(var V: TVarData);
+procedure TDataRow.Clear(var V: TVarData);
 begin
-  Finalize(TTableRowData(V));
+  Finalize(TDataRowData(V));
   FillChar(V, SizeOf(V), #0);
 end;
 
-procedure TTableRow.Copy(var Dest: TVarData; const Source: TVarData;
+procedure TDataRow.Copy(var Dest: TVarData; const Source: TVarData;
   const Indirect: Boolean);
 var
-  LDestRow: TTableRowData absolute Dest;
-  LSourceRow: TTableRowData absolute Source;
+  LDestRow: TDataRowData absolute Dest;
+  LSourceRow: TDataRowData absolute Source;
 begin
   if Indirect then
     SimplisticCopy(Dest,Source,true)
@@ -357,21 +330,22 @@ begin
     FillChar(LDestRow, SizeOf(LDestRow), #0);
     LDestRow.vtype := LSourceRow.vtype;
     LDestRow.vcolumnmap := LSourceRow.vcolumnmap;
-    LDestRow.vvalues := system.copy(TTableRowData(LSourceRow).vvalues);
+    LDestRow.vvalues := system.copy(TDataRowData(LSourceRow).vvalues);
   end;
 end;
 
-function TTableRow.DoFunction(var Dest: TVarData; const V: TVarData;
+function TDataRow.DoFunction(var Dest: TVarData; const V: TVarData;
   const Name: string; const Arguments: TVarDataArray): Boolean;
 var
-  LRow: TTableRowData absolute V;
+  LRow: TDataRowData absolute V;
 begin
   Result := (Name = '_') and (Length(Arguments)=1);
   if Result then
     Variant(Dest) := LRow.vvalues[Variant(Arguments[0])];
 end;
 
-class function TTableRow.New(AColumns: PTableColumns): Variant;
+class function TDataRow.New(const ADataSourceClassName:AnsiString; const AColumns: TDataColumns): Variant;
+
 var
   LColumnMap: TColumnMapToIndex;
 begin
@@ -380,21 +354,13 @@ begin
 
   VarClear(Result);
   FillChar(Result, SizeOf(Result), #0);
-  TTableRowData(Result).vtype:=TableRowType.VarType;
-
-  if not FColumns.TryGetValue(AColumns, LColumnMap) then
-    LColumnMap := MapColumns(AColumns);
+  TDataRowData(Result).vtype:=DataRowType.VarType;
 
-  TTableRowData(Result).vcolumnmap:=LColumnMap;
-  SetLength(TTableRowData(Result).vvalues, LColumnMap.Count);
-end;
-
-{ TSearchCapability }
+  if not FColumns.TryGetValue(ADataSourceClassName, LColumnMap) then
+    LColumnMap := MapColumns(ADataSourceClassName, AColumns);
 
-class function TSearchCapability.From(const AName : utf8string; AFilterType : TVisualGridFilters) : TSearchCapability;
-begin
-  Result.ColumnName := AName;
-  Result.SupportedFilters := AFilterType;
+  TDataRowData(Result).vcolumnmap:=LColumnMap;
+  SetLength(TDataRowData(Result).vvalues, LColumnMap.Count);
 end;
 
 { TCustomDataSource }
@@ -403,13 +369,20 @@ constructor TCustomDataSource<T>.Create(AOwner: TComponent);
 begin
   inherited Create(AOwner);
   FLock := TCriticalSection.Create;
-  FColumns := GetColumns;
+  FClassName := Self.ClassName;
 end;
 
+{constructor TCustomDataSource<T>.Create(AOwner: TComponent; const ADataSourceClassName : AnsiString);
+begin
+  inherited Create(AOwner);
+  FLock := TCriticalSection.Create;
+  FClassName := ADataSourceClassName;
+end;}
+
 destructor TCustomDataSource<T>.Destroy;
 var
   i : integer;
-  policy : TItemDisposePolicy;
+  policy : TDisposePolicy;
 begin
   inherited;
   FreeAndNil(FLock);
@@ -548,13 +521,13 @@ begin
      // Dehydrate the page of data only
 
      // Set columns
-     ADataTable.Columns := FColumns;
+     ADataTable.Columns := GetColumns;
 
      if pageEnd >= pageStart then begin
        j := 0;
        SetLength(ADataTable.Rows, pageEnd - pageStart + 1);
        for i := pageStart to pageEnd do begin
-         ADataTable.Rows[j] := TTableRow.New(@FColumns);
+         ADataTable.Rows[j] := TDataRow.New(FClassName, ADataTable.Columns);
          DehydrateItem( data[i], ADataTable.Rows[j]);
          ADataTable.Rows[j].__KEY := GetEntityKey(data[i]);
          inc(j)
@@ -1035,9 +1008,9 @@ begin
 end;
 
 initialization
-  TableRowType := TTableRow.Create;
+  DataRowType := TDataRow.Create;
 
 finalization
-  TableRowType.Free;
+  DataRowType.Free;
 end.
 

+ 0 - 4
src/libraries/sphere10/UCommon.pas

@@ -235,10 +235,6 @@ type
      function WithinMilliseconds(const aDateTime: TDateTime; const AMilliseconds: Int64): Boolean; inline;
    end;
 
-  { TItemDisposePolicy }
-
-  TItemDisposePolicy = (idpNone, idpNil, idpFreeAndNil);
-
   { Event Support}
 
   TNotifyEventEx = procedure (sender : TObject; const args: array of Pointer) of object;

+ 66 - 18
src/libraries/sphere10/UMemory.pas

@@ -22,6 +22,10 @@ interface
 
 type
 
+  { TDisposePolicy }
+
+  TDisposePolicy = (idpNone, idpNil, idpFree, idpFreeAndNil, idpRelease, idpFreeMem);
+
   { TDisposables }
 
   TDisposables = record
@@ -30,7 +34,7 @@ type
 
        TDisposablePointer = record
          Ptr: Pointer;
-         IsObject: Boolean;
+         DisposePolicy : TDisposePolicy;
        end;
 
       TGuard = class(TInterfacedObject)
@@ -46,7 +50,7 @@ type
       FLastIndex: Integer;
       class procedure Initialize(var ADisposables: TDisposables); static;
       class procedure Finalize(var ADisposables: TDisposables); static;
-      procedure RegisterPointer(Ptr: Pointer; IsObject: Boolean);
+      procedure RegisterPointer(Ptr: Pointer; ADisposePolicy: TDisposePolicy);
       procedure UnregisterPointer(Ptr: Pointer);
     public
       function AddObject(const AnObject: TObject): TObject;
@@ -70,6 +74,7 @@ type
         private
           FOwner: __PAutoPtr_T;
         public
+          FDisposePolicy : TDisposePolicy;
           constructor Create(AAutoPtrRec: __PAutoPtr_T);
           destructor Destroy; override;
       end;
@@ -79,16 +84,20 @@ type
       class procedure Initialize(var AAutoPtr: __TAutoPtr_T); static;
       class procedure Finalize(var AAutoPtr: __TAutoPtr_T); static;
       function GetPointer : Pointer; inline;
-      procedure SetPointer(Ptr: Pointer); inline;
+      procedure SetPointer(Ptr: Pointer); inline; overload;
       function GetObject : TObject; inline;
-      procedure SetObject(const AnObject: TObject); inline;
+      procedure SetObject(const AnObject: TObject); inline; overload;
       function GetValue : T; inline;
-      procedure SetValue(const AValue : T); inline;
+      procedure SetValue(const AValue : T); inline; overload;
       procedure CheckGuard; inline;
     public
       property Pointer_ : Pointer read GetPointer write SetPointer;
       property Object_ : TObject read GetObject write SetObject;
       property Value : T read GetValue write SetValue;
+      procedure SetPointer(Ptr: Pointer; ADisposePolicy : TDisposePolicy); inline; overload;
+      procedure SetObject(const AnObject: TObject; ADisposePolicy : TDisposePolicy); inline; overload;
+      procedure SetValue(const AValue : T; ADisposePolicy : TDisposePolicy); inline; overload;
+      procedure DangerousUpdateDisposePolicy(ADisposePolicy : TDisposePolicy);
       procedure Clear;
   end;
 
@@ -130,11 +139,18 @@ begin
 
   for i := ADisposables.FLastIndex downto 0 do
   try
-    if ADisposables.FPointers[i].IsObject then
-      TObject(ADisposables.FPointers[i].Ptr).Free
-    else begin
-      if Assigned(ADisposables.FPointers[i].Ptr) then
-        System.FreeMem(ADisposables.FPointers[i].Ptr);
+    case ADisposables.FPointers[i].DisposePolicy of
+      idpNone: ;
+      idpNil: ADisposables.FPointers[i].Ptr := nil;
+      idpFree, idpFreeAndNil: if Assigned(ADisposables.FPointers[i].Ptr) then begin
+        FreeAndNil(ADisposables.FPointers[i].Ptr);
+        ADisposables.FPointers[i].Ptr := nil;
+      end;
+      idpRelease: begin
+        raise ENotSupportedException.Create('Dispose policy idoRelease not supported');
+      end;
+      idpFreeMem: if Assigned(ADisposables.FPointers[i].Ptr) then System.FreeMem(ADisposables.FPointers[i].Ptr);
+      else raise ENotSupportedException.Create('Dispose policy not supported');;
     end;
   except
     if not Assigned(FirstException) then
@@ -148,14 +164,14 @@ begin
   end;
 end;
 
-procedure TDisposables.RegisterPointer(Ptr: Pointer; IsObject: Boolean);
+procedure TDisposables.RegisterPointer(Ptr: Pointer; ADisposePolicy: TDisposePolicy);
 begin
   if FLastIndex > High(FPointers) then
     SetLength(FPointers, Length(FPointers) * 2);
 
   Inc(FLastIndex);
   FPointers[FLastIndex].Ptr := Ptr;
-  FPointers[FLastIndex].IsObject := IsObject;
+  FPointers[FLastIndex].DisposePolicy := ADisposePolicy;
 end;
 
 procedure TDisposables.UnregisterPointer(Ptr: Pointer);
@@ -173,7 +189,7 @@ function TDisposables.AddObject(const AnObject: TObject): TObject;
 begin
     if not Assigned(FGuardian) then
       FGuardian := TGuard.Create(@Self);
-  RegisterPointer(Pointer(AnObject), True);
+  RegisterPointer(Pointer(AnObject), idpFree);
   Result := AnObject;
 end;
 
@@ -202,7 +218,7 @@ procedure TDisposables.AddMem(const P: Pointer);
 begin
   if not Assigned(FGuardian) then
     FGuardian := TGuard.Create(@Self);
-  RegisterPointer(P, False);
+  RegisterPointer(P, idpFreeMem);
 end;
 
 procedure TDisposables.ReallocMem(var P: Pointer; Size:Integer);
@@ -213,7 +229,7 @@ begin
     FGuardian := TGuard.Create(@Self);
 
   for i := FLastIndex downto 0 do
-    if not FPointers[i].IsObject and (FPointers[i].Ptr = P) then
+    if (FPointers[i].DisposePolicy = idpFreeMem) and (FPointers[i].Ptr = P) then
     begin
       System.ReallocMem(FPointers[i].Ptr, Size);
       P := FPointers[i].Ptr;
@@ -233,9 +249,16 @@ end;
 constructor TAutoPtr<T>.TGuard.Create(AAutoPtrRec: __PAutoPtr_T);
 begin
   FOwner := AAutoPtrRec;
+  FDisposePolicy := idpNone;
   TAutoPtr<T>.Initialize(FOwner^);
 end;
 
+procedure TAutoPtr<T>.DangerousUpdateDisposePolicy(ADisposePolicy : TDisposePolicy);
+begin
+  CheckGuard;
+  TGuard(FGuardian).FDisposePolicy := ADisposePolicy;
+end;
+
 destructor TAutoPtr<T>.TGuard.Destroy;
 begin
   inherited;
@@ -262,7 +285,15 @@ procedure TAutoPtr<T>.Clear;
 begin
   CheckGuard;
   if FPointer <> nil then begin
-    TObject(FPointer).Free;
+    case TGuard(FGuardian).FDisposePolicy of
+      idpNone: ;
+      idpNil: FPointer := nil;
+      idpFree, idpFreeAndNil: FreeAndNil ( FPointer );
+      idpRelease: raise ENotSupportedException.Create('Dispose policy idpRelease not supported');
+      idpFreeMem: System.FreeMem(FPointer);
+      else raise ENotSupportedException.Create('Dispose policy not supported');
+    end;
+
     FPointer := nil;
     // avoid FGuard nullifcation due to recursive calls
   end;
@@ -275,11 +306,17 @@ begin
 end;
 
 procedure TAutoPtr<T>.SetPointer(Ptr: Pointer);
+begin
+  SetPointer(Ptr, idpFree);
+end;
+
+procedure TAutoPtr<T>.SetPointer(Ptr: Pointer; ADisposePolicy : TDisposePolicy);
 begin
   CheckGuard;
   if FPointer <> nil then
     Clear;
   FPointer := Ptr;
+  TGuard(FGuardian).FDisposePolicy := ADisposePolicy;
 end;
 
 function TAutoPtr<T>.GetObject : TObject;
@@ -289,7 +326,13 @@ end;
 
 procedure TAutoPtr<T>.SetObject(const AnObject: TObject);
 begin
-  SetPointer( Pointer(AnObject));
+  SetObject(AnObject , idpFree);
+end;
+
+
+procedure TAutoPtr<T>.SetObject(const AnObject: TObject; ADisposePolicy : TDisposePolicy);
+begin
+  SetPointer( Pointer(AnObject), ADisposePolicy);
 end;
 
 function TAutoPtr<T>.GetValue : TObject;
@@ -299,7 +342,12 @@ end;
 
 procedure TAutoPtr<T>.SetValue(const AValue: T);
 begin
-  SetObject(TObject(AValue));
+  SetValue(AValue, idpFree);
+end;
+
+procedure TAutoPtr<T>.SetValue(const AValue: T; ADisposePolicy : TDisposePolicy);
+begin
+  SetObject(TObject(AValue), ADisposePolicy);
 end;
 
 procedure TAutoPtr<T>.CheckGuard;

+ 141 - 124
src/libraries/sphere10/UVisualGrid.pas

@@ -7,8 +7,8 @@
   or visit http://www.opensource.org/licenses/mit-license.php.
 
   Credits:
-     Herman Schoenfeld (designer)
-     Maciej Izak (hnb) (developer)
+     Herman Schoenfeld
+     Maciej Izak (hnb)
 }
 
 unit UVisualGrid;
@@ -26,11 +26,7 @@ uses
   UCommon, UCommon.Data, UCommon.Collections, Generics.Collections, Generics.Defaults, Menus, ComboEx, Buttons, Math,
   LResources, syncobjs;
 
-const
-  CT_VISUALGRID_DEFAULT = Integer(-1);   { Column is not sized on start }
-  CT_VISUALGRID_STRETCH = Integer(-2);   { Column is stretched to fit }
-
-type
+ type
 
   { TSelectionType }
 
@@ -61,6 +57,9 @@ type
   end;
 
   TCustomVisualGrid = class;
+  TVisualColumn = class;
+
+  TVisualColumnRenderer = procedure(Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean) of object;
 
   { TVisualColumn }
 
@@ -76,22 +75,40 @@ type
     FGrid: TCustomVisualGrid;
     FSortDirection: TSortDirection;
     FIgnoreRefresh: Boolean;
+
+    FIndex : Integer;
+    FName : utf8string;
+    FFilters : TDataFilters;
+    FBinding : AnsiString;
+    FWidth : Integer;
+    FStretchColumn : Boolean;
+    FAutoWidth : boolean;
+    FRenderer : TVisualColumnRenderer;
+    FVisible : boolean; // TODO: implement this functionality
+    FSortSequence : Integer; // TODO: implement this functionality
   public
+    property Index : Integer read FIndex write FIndex;
+    property Name : utf8string read FName write FName;
+    property Filters : TDataFilters read FFilters write FFilters;
+    property Binding : AnsiString read FBinding write FBinding;
+    property AutoWidth : boolean read FAutoWidth write FAutoWidth;
+    property Visible : boolean read FVisible write FVisible;
+    property SortSequence : Integer read FSortSequence write FSortSequence;
     property InternalColumn : TGridColumn read FColumn;
-    constructor Create(AGrid: TCustomVisualGrid; AColumn: TGridColumn);
+    constructor Create(AGrid: TCustomVisualGrid);
     property StretchedToFill: boolean read GetStretchedToFill write SetStretchedToFill;
     property Width: Integer read GetWidth write SetWidth;
     property SortDirection: TSortDirection read FSortDirection write SetSortDirection;
+    property Renderer : TVisualColumnRenderer read FRenderer write FRenderer;
+    public procedure AttachGridColumn(AGridColumn : TGridColumn);
   end;
 
-  TVisualColumnRenderer = procedure(Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData: Variant; var Handled: boolean) of object;
-
   { TVisualGrid Events }
 
   TPreparePopupMenuEvent = procedure(Sender: TObject; constref ASelection: TVisualGridSelection; out APopupMenu: TPopupMenu) of object;
   TSelectionEvent = procedure(Sender: TObject; constref ASelection: TVisualGridSelection) of object;
-  TDrawVisualCellEvent = procedure(Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData: Variant; var Handled: boolean) of object;
-  TColumnInitializeEvent = procedure(Sender: TObject; AColIndex:Integer; AColumn : TVisualColumn) of object;
+  TDrawVisualCellEvent = procedure(Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean) of object;
+  TColumnInitializeEvent = procedure(Sender: TObject; AColumn : TVisualColumn) of object;
 
   { TVisualGrid Exceptions }
 
@@ -99,8 +116,7 @@ type
 
   { TVisualGridOptions }
 
-  TVisualGridOptions = set of (vgoColAutoFill, vgoColSizing, vgoAutoHidePaging,
-    vgoMultiSearchCheckComboBox, vgoSortDirectionAllowNone);
+  TVisualGridOptions = set of (vgoColAutoFill, vgoColSizing, vgoAutoHidePaging, vgoMultiSearchCheckComboBox, vgoSortDirectionAllowNone);
 
   { TSortMode }
 
@@ -160,7 +176,7 @@ type
       procedure SetEditVisible(AValue: boolean);
       procedure SetVisible(AValue: boolean);
     public
-      SearchCapability: PSearchCapability;
+      Column: TVisualColumn;
       constructor Create(AParent: TWinControl; AGrid: TCustomVisualGrid);
       destructor Destroy; override;
       procedure Clear;
@@ -258,7 +274,7 @@ type
     FWidgetControlParent: TWinControl;
     function GetCells(ACol, ARow: Integer): Variant;
     function GetColCount: Integer; inline;
-    function GetColumns(Index: Integer): TVisualColumn;
+    function GetColumn(Index: Integer): TVisualColumn;
     function GetActiveDataTable: PDataTable;
     function GetRowCount: Integer; inline;
     function GetRows(ARow: Integer): Variant;
@@ -296,7 +312,6 @@ type
     FDataSource: IDataSource;
     FStrFilter: UTF8String;
     FFilters: TList<TColumnFilter>;
-    FSearchCapabilities: TSearchCapabilities;
     FPageSize: Integer;
     FPageIndex: Integer;
     FPageCount: Integer;
@@ -305,8 +320,6 @@ type
     FLastFetchDataResult: TLastFetchDataResult;
     FSortColumn: TVisualColumn;
     FDefaultStretchedColumn : Integer;
-    FDefaultColumnWidths : TArray<Integer>;
-    FColumnRenderers : TArray<TVisualColumnRenderer>;
 
     FOnDrawVisualCell: TDrawVisualCellEvent;
     FOnColumnInitialize : TColumnInitializeEvent;
@@ -320,8 +333,7 @@ type
     // return true if range is correct
     function CheckRangeForPageSize(var APageSize: Integer): boolean;
     procedure SetDataSource(ADataSource: IDataSource);
-    procedure DoDrawCell(Sender: TObject; ACol, ARow: Longint;
-      Rect: TRect; State: TGridDrawState; const RowData: Variant);
+    procedure DoDrawCell(Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState; const RowData: Variant);
     procedure RefreshPageIndexAndGridInterface;
     procedure RefreshPageIndexData(ARefreshColumns: boolean);
     procedure ResizeSearchEdit(ACol: Integer);
@@ -344,8 +356,6 @@ type
     property ShowAllData: boolean read FShowAllData write SetShowAllData default false;
     property FetchDataInThread: boolean read FFetchDataInThread write SetFetchDataInThread;
     property DefaultStretchedColumn: Integer read FDefaultStretchedColumn write FDefaultStretchedColumn;
-    property DefaultColumnWidths : TArray<Integer> read FDefaultColumnWidths write FDefaultColumnWidths;
-    property ColumnRenderers : TArray<TVisualColumnRenderer> read FColumnRenderers write FColumnRenderers;
 
     property CanPage: boolean read FCanPage write SetCanPage default true;
     property CanSearch: boolean read FCanSearch write SetCanSearch default true;
@@ -361,7 +371,7 @@ type
     property Caption: TVisualGridCaption read FCaption write FCaption;
     property CellPadding : TRect read FCellPadding write FCellPadding;
     property ColCount: Integer read GetColCount;
-    property Columns[Index: Integer]: TVisualColumn read GetColumns;
+    property Columns[Index: Integer]: TVisualColumn read GetColumn;
     property Cells[ACol, ARow: Integer]: Variant read GetCells write SetCells;
     property RowCount: Integer read GetRowCount;
     property Rows[ARow: Integer]: Variant read GetRows write SetRows;
@@ -374,6 +384,7 @@ type
 
     property WidgetControl: TControl read FWidgetControl write SetWidgetControl;
     property InternalDrawGrid : TDrawGrid read FDrawGrid;
+    function AddColumn(const AName: utf8string) : TVisualColumn;
     procedure RefreshGrid;
   end;
 
@@ -406,7 +417,7 @@ type
 
   TVisualCellRenderers = class
     public
-      class procedure DollarValue (Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData: Variant; var Handled: boolean);
+      class procedure DollarValue (Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
   end;
 
   { TVisualGridSearchExpressionService }
@@ -541,7 +552,6 @@ procedure TVisualColumn.SetSortDirection(AValue: TSortDirection);
 begin
   if FSortDirection=AValue then Exit;
   FSortDirection:=AValue;
-
   TDrawGridAccess(FGrid.FDrawGrid).InvalidateCell(FColumn.Index, 0, true);
   if not FIgnoreRefresh then
     FGrid.RefreshPageIndexData(false);
@@ -549,18 +559,38 @@ end;
 
 procedure TVisualColumn.SetStretchedToFill(AValue: boolean);
 begin
-  FColumn.SizePriority := ifthen(AValue, 1);
+  FStretchColumn:=AValue;
+  if Assigned(FColumn) then
+    FColumn.SizePriority := ifthen(AValue, 1);
 end;
 
 procedure TVisualColumn.SetWidth(AValue: Integer);
 begin
-  FColumn.Width := AValue;
+  if Self.AutoWidth then
+    Self.StretchedToFill := false
+  else if Self.StretchedToFill then
+    Self.StretchedToFill := true
+  else begin
+    Self.StretchedToFill := False;
+    if Assigned(FColumn) then
+      FColumn.Width := AValue;
+  end;
+  FWidth := AValue;
 end;
 
-constructor TVisualColumn.Create(AGrid: TCustomVisualGrid; AColumn: TGridColumn );
+constructor TVisualColumn.Create(AGrid: TCustomVisualGrid);
 begin
   FGrid := AGrid;
-  FColumn := AColumn;
+  FColumn := nil;
+end;
+
+procedure TVisualColumn.AttachGridColumn(AGridColumn : TGridColumn);
+begin
+  Self.FColumn := AGridColumn;
+  Self.AutoWidth := Self.AutoWidth;
+  Self.StretchedToFill := Self.StretchedToFill;
+  Self.Width := Self.Width;
+  Self.FColumn.Title.Caption:=''; // already painted in default drawing event
 end;
 
 { TVisualGridSelection }
@@ -595,7 +625,7 @@ end;
 
 { TVisualCellRenderers }
 
-class procedure TVisualCellRenderers.DollarValue (Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData: Variant; var Handled: boolean);
+class procedure TVisualCellRenderers.DollarValue (Sender: TObject; ACol, ARow: Longint; Canvas: TCanvas; Rect: TRect; State: TGridDrawState; const CellData, RowData: Variant; var Handled: boolean);
 var
   LReal : Real;
   LTextStyle: TTextStyle;
@@ -941,7 +971,7 @@ begin
 
       FSearchButton := TSpeedButton.Create(Self);
       FSearchButton.Parent := FTopPanelRight;
-      // HS: remove windres usage due to "directory with spaces" bug during compilation.
+      // remove windres usage due to "directory with spaces" bug during compilation.
       //{$IFDEF WINDOWS}
       //FSearchButton.LoadGlyphFromResourceName(HINSTANCE, 'VISUALGRID_SEARCH');
       //{$ELSE}
@@ -1184,11 +1214,8 @@ var
 begin
   if ARow = 0 then
   begin
-    if ACol < Length(ActiveDataTable.Columns) then
-      LText := ActiveDataTable.Columns[ACol]
-    else
-      raise EVisualGridError.CreateFmt(sImproperColumnIndex, [Length(ActiveDataTable.Columns)-1,ACol]);
-    LColumn := GetColumns(ACol);
+    LColumn := GetColumn(ACol);
+    LText := LColumn.Name;
     Rect := CalculateCellContentRect(Rect);
 
     FDrawGrid.Canvas.TextRect(Rect, Rect.Left, Rect.Top, LText);
@@ -1245,7 +1272,6 @@ procedure TCustomVisualGrid.SortDirectionGlyphRefresh;
   procedure VisualColumnsSortDirection(AStopOnFirstSortable, AAllNone: boolean);
   var
     i: integer;
-    p: PSearchCapability;
     LStop: boolean = false;
     LColumn: TVisualColumn;
   begin
@@ -1259,9 +1285,6 @@ procedure TCustomVisualGrid.SortDirectionGlyphRefresh;
         LColumn.SortDirection := sdNone;
         Continue;
       end;
-      p := FMultiSearchEdits[i].SearchCapability;
-      if Assigned(p) and (vgfSortable in p^.SupportedFilters) then
-      begin
         if not LStop then
           if vgoSortDirectionAllowNone in Options then
             LColumn.SortDirection := sdNone
@@ -1275,8 +1298,6 @@ procedure TCustomVisualGrid.SortDirectionGlyphRefresh;
           FSortColumn := LColumn;
           LStop := true;
         end;
-      end else
-        LColumn.SortDirection := sdNone;
     finally
       LColumn.FIgnoreRefresh:=false;
     end;
@@ -1333,28 +1354,29 @@ var
   LFormat: TFormatSettings;
   LNewStrFilter: utf8string;
   LException: boolean = false;
+  LNum : SizeInt;
+  LDataFilter : TDataFilter;
   e: TSearchEdit;
 
-  procedure AddExpression(const AExpression: utf8string;
-    ASearchCapability: PSearchCapability);
+  procedure AddExpression(const AExpression: utf8string; AColumn : TVisualColumn);
   var
-    LSearchCapability: TSearchCapability;
-    LAccepted: TVisualGridFilters;
+    LColumn: TVisualColumn;
+    LAccepted: TDataFilters;
     LColumnFilter: TColumnFilter;
-    LCandidates: TVisualGridFilters = [];
+    LCandidates: TDataFilters = [];
     LExpressionRecord: TExpressionRecord;
 
-    procedure AddFilter(ASearchCapability: PSearchCapability);
+    procedure AddFilter( const ABinding : AnsiString; const AFilters: TDataFilters);
     var
-      LFilter: TVisualGridFilter;
+      LFilter: TDataFilter;
     begin
-      LAccepted := ASearchCapability.SupportedFilters * LCandidates;
+      LAccepted := AFilters * LCandidates;
       if LAccepted = [] then
         Exit;
       for LFilter in LAccepted do
       begin
         LColumnFilter:=Default(TColumnFilter);
-        LColumnFilter.ColumnName := ASearchCapability.ColumnName;
+        LColumnFilter.ColumnName := ABinding;
         LColumnFilter.Filter := LFilter;
 
         if LFilter in TEXT_FILTER then
@@ -1435,11 +1457,11 @@ var
       Assert(LCandidates<>[]);
     end;
 
-    if Assigned(ASearchCapability) then
-      AddFilter(ASearchCapability)
+    if Assigned(AColumn) then
+      AddFilter(AColumn.Binding, AColumn.Filters)    // add filter for column only
     else
-      for LSearchCapability in FSearchCapabilities do
-        AddFilter(@LSearchCapability);
+      for LColumn in FColumns do
+        AddFilter(LColumn.Binding, LColumn.Filters); // add filter for all columns
 
     LNewStrFilter := LNewStrFilter + QuotedStr(AExpression) + ',';
   end;
@@ -1454,9 +1476,13 @@ begin
   try
     AddExpression(FSearchEdit.Text, nil);
     // multi column search
-    for e in FMultiSearchEdits do
-      if Assigned(e.SearchCapability) then
-        AddExpression(e.FEdit.Text, e.SearchCapability);
+    for e in FMultiSearchEdits do begin
+      // Count how many filters
+      LNum := 0;
+      for LDataFilter in e.Column.Filters do inc(LNum);
+      if LNum > 0 then
+        AddExpression(e.FEdit.Text, e.Column);
+    end;
   finally
     // delete last comma
     SetLength(LNewStrFilter, Length(LNewStrFilter)-1);
@@ -1494,7 +1520,7 @@ begin
   Result := FColumns.Count;
 end;
 
-function TCustomVisualGrid.GetColumns(Index: Integer): TVisualColumn;
+function TCustomVisualGrid.GetColumn(Index: Integer): TVisualColumn;
 begin
   Result := FColumns[Index];
 end;
@@ -1629,8 +1655,12 @@ begin
 end;
 
 procedure TCustomVisualGrid.SetCells(ACol, ARow: Integer; AValue: Variant);
+var LBinding : AnsiString; LDataColIndex : Integer; LRow : TDataRowData;
 begin
-  TTableRowData(FDataTable.Rows[ARow]).vvalues[ACol] := AValue;
+  LRow := TDataRowData(FDataTable.Rows[ARow]);
+  LBinding := FColumns[ACol].Binding;
+  LDataColIndex := LRow.vcolumnmap[LBinding];
+  LRow.vvalues[LDataColIndex] := AValue;
   TDrawGridAccess(FDrawGrid).InvalidateCell(ACol, ARow, true);
 end;
 
@@ -1807,9 +1837,7 @@ var
   LEditOnLeft, LEditOnRight: TSearchEdit;
   //LFixedRect: TRect;
   LRect: TRect;
-  xxx : SizeInt;
 begin
-  xxx := FMultiSearchEdits.Count;
   LEdit := FMultiSearchEdits[ACol];
   LEdit.Visible := FDrawGrid.IsFixedCellVisible(aCol, 0);
   if ACol > 0 then
@@ -1893,74 +1921,57 @@ begin
     FOnFinishedUpdating(Self);
 end;
 
+function TCustomVisualGrid.AddColumn(const AName: utf8string) : TVisualColumn;
+begin
+  Result := TVisualColumn.Create(Self);
+  Result.Index := FColumns.Count;
+  Result.Name := AName;
+  Result.Binding := UTF8Decode(AName);
+  if Result.Index = FDefaultStretchedColumn then
+    Result.StretchedToFill := true;
+  Result.FRenderer := nil;
+  Result.FVisible := true;
+  Result.FSortSequence := -1;
+  Result.Filters:=[];
+  FColumns.Add(Result);
+  Result.AttachGridColumn(FDrawGrid.Columns.Add);  // attach an underlyinh drawgrid
+end;
+
 procedure TCustomVisualGrid.ReloadColumns;
 var
   i: Integer;
   LEdit: TSearchEdit;
-  p: PSearchCapability;
   LColumn: TVisualColumn;
-
-  function SearchCapability: PSearchCapability;
-  var
-    j: Integer;
-  begin
-    for j := 0 to High(FSearchCapabilities) do
-      if FSearchCapabilities[j].ColumnName = FDataTable.Columns[i] then
-        Exit(@FSearchCapabilities[j]);
-    Result := nil;
-  end;
-
 begin
   FSortColumn := nil;
-  FDrawGrid.Columns.Clear;
+  FDrawGrid.Columns.Clear; // clear TDrawGrid columns
   FDrawGrid.Columns.BeginUpdate;
-  FColumns.Clear;
-  for i := 0 to High(FDataTable.Columns) do
-  begin
-    LColumn := TVisualColumn.Create(Self, FDrawGrid.Columns.Add);
-    FColumns.Add(LColumn);
-    if i <= High(FDefaultColumnWidths) then
-      case FDefaultColumnWidths[i] of
-        CT_VISUALGRID_DEFAULT: LColumn.StretchedToFill:=False;
-        CT_VISUALGRID_STRETCH: LColumn.StretchedToFill:=True;
-        else begin
-          LColumn.StretchedToFill := False;
-          LColumn.SetWidth(FDefaultColumnWidths[i]);
-        end;
-    end else if i = FDefaultStretchedColumn then LColumn.StretchedToFill:=True;
-    LColumn.FColumn.Title.Caption:=''; //FDataTable.Columns[i]; already painted in default drawing event
-
+  for i := 0 to FColumns.Count - 1 do begin
+    LColumn := FColumns[i];
+    LColumn.AttachGridColumn(FDrawGrid.Columns.Add);  // recreate TDrawGrid column
     // invoke client initialization
     if Assigned(FOnColumnInitialize) then
-      FOnColumnInitialize(Self, i, LColumn);
+      FOnColumnInitialize(Self, LColumn);
   end;
   FDrawGrid.Columns.EndUpdate;
   // TODO: may be optimized
   FMultiSearchEdits.Clear;
   FSearchButton.Visible := true;
   FMultiSearchCheckComboBox.Clear;
-  FSearchCapabilities := Copy(FDataSource.SearchCapabilities);
-  for i := 0 to High(FDataTable.Columns) do
-  begin
+  for i := 0 to FColumns.Count - 1 do begin
     LEdit := TSearchEdit.Create(FTopPanelMultiSearchClient, Self);
     FMultiSearchEdits.Add(LEdit);
-    p := SearchCapability;
-    LEdit.EditVisible:=Assigned(p) and FMultiSearchMenuItem.Checked and (p^.SupportedFilters * NON_SORTABLE_FILTER <> []);
-    LEdit.SearchCapability := p;
+    LEdit.Column := FColumns[i];
+    LEdit.EditVisible := FMultiSearchMenuItem.Checked AND (FColumns[i].Filters * NON_SORTABLE_FILTER <> []);
     ResizeSearchEdit(i);
-    if Assigned(p) then
+    if FColumns[i].Filters * NON_SORTABLE_FILTER <> [] then
     begin
-      if p^.SupportedFilters * NON_SORTABLE_FILTER <> [] then
-      begin
-      FMultiSearchCheckComboBox.AddItem(p^.ColumnName, cbChecked);
+      FMultiSearchCheckComboBox.AddItem(FColumns[i].Name, cbChecked);
       FMultiSearchCheckComboBox.Objects[FMultiSearchCheckComboBox.Items.Count-1] := LEdit;
       FMultiSearchCheckComboBox.Checked[FMultiSearchCheckComboBox.Items.Count-1] := FMultiSearchMenuItem.Checked;
     end;
-      if (vgfSortable in p^.SupportedFilters)
-       and not (vgoSortDirectionAllowNone in Options)
-       and (SortMode <> smNone) then
-        FColumns[i].SortDirection := sdAscending;
-    end;
+    if (vgfSortable in FColumns[i].Filters) and not (vgoSortDirectionAllowNone in Options) and (SortMode <> smNone) then
+      FColumns[i].SortDirection := sdAscending;
   end;
   if FDrawGrid.Columns.Count > 0 then
     FTopPanelMultiSearch.Height:=FMultiSearchEdits.Last.FPanel.Height;
@@ -2068,7 +2079,6 @@ procedure TCustomVisualGrid.FetchPage(out AResult: TPageFetchResult);
     LColumnsToAdd: TList<Integer>;
     i, j, idx: Integer;
     LFilter: TColumnFilter;
-    LData: PDataTable;
 
     function UpdateFilterSortDirection(const AColumnName: utf8string; ASortDirection: TSortDirection): boolean;
     var
@@ -2091,17 +2101,16 @@ procedure TCustomVisualGrid.FetchPage(out AResult: TPageFetchResult);
     for i := FFilters.Count-1 downto 0 do
       if FFilters[i].Filter=vgfSortable then
         FFilters.Delete(i);
-    LData := GetActiveDataTable;
     try
       for i := 0 to FColumns.Count - 1 do
         if FColumns[i].SortDirection <> sdNone then
         begin
           // try to find column in existing filters
           // if filter not found we need to create it later
-          if not UpdateFilterSortDirection(LData.Columns[i], FColumns[i].SortDirection) then
+          if not UpdateFilterSortDirection(FColumns[i].Binding, FColumns[i].SortDirection) then
             LColumnsToAdd.Add(i);
         end else
-          UpdateFilterSortDirection(LData.Columns[i], sdNone);
+          UpdateFilterSortDirection(FColumns[i].Binding, sdNone);
 
       // add missing filters
       FFilters.Count:=FFilters.Count + LColumnsToAdd.Count;
@@ -2114,7 +2123,8 @@ procedure TCustomVisualGrid.FetchPage(out AResult: TPageFetchResult);
         // create new filter
         LFilter := Default(TColumnFilter);
         LFilter.Filter:=vgfSortable;
-        LFilter.ColumnName:=LData.Columns[idx];
+        LFilter.ColumnName:= FColumns[idx].Binding;
+        LFilter.SortSequence := FColumns[idx].SortSequence;
         LFilter.Sort := FColumns[idx].SortDirection;
         FFilters[i] := LFilter;
       end;
@@ -2267,8 +2277,11 @@ procedure TCustomVisualGrid.StandardDrawCell(Sender: TObject; ACol,
   ARow: Longint; Rect: TRect; State: TGridDrawState);
 var
   LHandled: boolean;
-  LCellData: Variant;
-  LColRenderer : TVisualColumnRenderer;
+  LCellData, LRow: Variant;
+  LRowData: TDataRowData;
+  LColumn : TVisualColumn;
+  LDataColIndex : Integer;
+
 begin
   LHandled := False;
 
@@ -2277,21 +2290,25 @@ begin
 
   if ARow = 0 then
     ResizeSearchEdit(ACol);
-  if (ARow > 0) and Assigned(FDataSource) then
-    LCellData := ActiveDataTable^.Rows[ARow-1]._(ACol);
+
+  LColumn := FColumns[ACol];
+
+  if (ARow > 0) and Assigned(FDataSource) then begin
+    LRow := ActiveDataTable^.Rows[ARow-1];
+    LRowData := TDataRowData(LRow);
+    LDataColIndex := LRowData.vcolumnmap[LColumn.Binding];
+    LCellData := LRowData.vvalues[LDataColIndex];
+  end;
 
   Rect := CalculateCellContentRect(Rect);
 
   // Try user-settable cell renderer
   if Assigned(FOnDrawVisualCell) then
-    FOnDrawVisualCell(Self, ACol, ARow, Canvas, Rect, State, LCellData, LHandled);
+    FOnDrawVisualCell(Self, ACol, ARow, Canvas, Rect, State, LCellData, LRow, LHandled);
 
   // Try user-settable column renderer
-  if (NOT LHandled) AND (ACol < Length(FColumnRenderers)) then begin
-    LColRenderer := FColumnRenderers[ACol];
-    if Assigned(LColRenderer) then
-      LColRenderer(Self, ACol, ARow, Canvas, Rect, State, LCellData, LHandled);
-  end;
+  if (NOT LHandled) AND Assigned(LColumn.Renderer) then
+      LColumn.Renderer(Self, ACol, ARow, Canvas, Rect, State, LCellData, LRow, LHandled);
 
   // Use default renderer
   if not LHandled then
@@ -2404,7 +2421,7 @@ begin
   if FSortMode = smNone then
     Exit;
 
-  LColumn := GetColumns(Index);
+  LColumn := GetColumn(Index);
   case LColumn.SortDirection of
     sdNone: LColumn.SortDirection := sdAscending;
     sdAscending: LColumn.SortDirection := sdDescending;

+ 2 - 5
src/pascalcoin_wallet.lpi

@@ -417,15 +417,12 @@
         <IsPartOfProject Value="True"/>
       </Unit73>
       <Unit74>
-        <Filename Value="gui\wizards\UWIZSendPASC_TransactionPayload.lfm"/>
+        <Filename Value="libraries\sphere10\UMemory.pas"/>
         <IsPartOfProject Value="True"/>
       </Unit74>
       <Unit75>
-        <Filename Value="gui\wizards\UWIZSendPASC_TransactionPayload.pas"/>
+        <Filename Value="gui\UCellRenderers.pas"/>
         <IsPartOfProject Value="True"/>
-        <ComponentName Value="WIZSendPASC_TransactionPayload"/>
-        <HasResources Value="True"/>
-        <ResourceBaseClass Value="Form"/>
       </Unit75>
     </Units>
   </ProjectOptions>