Browse Source

GUI: improved wallet refresh stability

Herman Schoenfeld 7 years ago
parent
commit
e0cd41aefa
4 changed files with 188 additions and 119 deletions
  1. 45 43
      src/core.utils/UDataSources.pas
  2. 133 74
      src/gui/UCTRLWallet.pas
  3. 4 0
      src/gui/UCellRenderers.pas
  4. 6 2
      src/pascalcoin_wallet.lpi

+ 45 - 43
src/core.utils/UDataSources.pas

@@ -6,7 +6,9 @@ unit UDataSources;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, UAccounts, UNode, UBlockchain, UCommon, UMemory, UConst, UCommon.Data, UCommon.Collections, Generics.Collections, Generics.Defaults, syncobjs;
+  Classes, SysUtils, UAccounts, UNode, UBlockchain, UCoreObjects,
+  UCommon, UMemory, UConst, UCommon.Data,
+  UCommon.Collections, Generics.Collections, Generics.Defaults, syncobjs;
 
 
 type
 type
 
 
@@ -22,27 +24,28 @@ type
   { TAccountsDataSource }
   { TAccountsDataSource }
 
 
   TAccountsDataSource = class(TAccountsDataSourceBase)
   TAccountsDataSource = class(TAccountsDataSourceBase)
-    public type
-      TOverview = record
-        TotalPASC : UInt64;
-        TotalPASA : Cardinal;
-      end;
-    private
-      FLastKnownUserAccounts : TArray<TAccount>;
-      FKeys : TSortedHashSet<TAccountKey>;
     protected
     protected
-      FLastOverview : TOverview;
+      FKeys : TSortedHashSet<TAccountKey>;
       function GetFilterKeys : TArray<TAccountKey>;
       function GetFilterKeys : TArray<TAccountKey>;
       procedure SetFilterKeys (const AKeys : TArray<TAccountKey>);
       procedure SetFilterKeys (const AKeys : TArray<TAccountKey>);
     public
     public
-      property Overview : TOverview read FLastOverview;
-      property LastFetchResult : TArray<TAccount> read FLastKnownUserAccounts;
       property FilterKeys : TArray<TAccountKey> read GetFilterKeys write SetFilterKeys;
       property FilterKeys : TArray<TAccountKey> read GetFilterKeys write SetFilterKeys;
       constructor Create(AOwner: TComponent); override;
       constructor Create(AOwner: TComponent); override;
       destructor Destroy; override;
       destructor Destroy; override;
       procedure FetchAll(const AContainer : TList<TAccount>); override;
       procedure FetchAll(const AContainer : TList<TAccount>); override;
   end;
   end;
 
 
+  { TFastAccountsDataSource }
+
+  TFastAccountsDataSource = class(TAccountsDataSource)
+    private
+      FPSummary : PUserSummary;
+    public
+      property Summary : PUserSummary read FPSummary write FPSummary;
+      constructor Create(AOwner: TComponent; APSummary : PUserSummary); overload;
+      procedure FetchAll(const AContainer : TList<TAccount>); override;
+  end;
+
   { TOperationsDataSourceBase }
   { TOperationsDataSourceBase }
 
 
   TOperationsDataSourceBase = class(TCustomDataSource<TOperationResume>)
   TOperationsDataSourceBase = class(TCustomDataSource<TOperationResume>)
@@ -91,7 +94,7 @@ type
 implementation
 implementation
 
 
 uses
 uses
-  math, UCoreUtils, UWallet, UUserInterface, UTime;
+  math, UCoreUtils, UWallet, UTime;
 
 
 { TAccountsDataSourceBase }
 { TAccountsDataSourceBase }
 
 
@@ -169,44 +172,43 @@ end;
 
 
 procedure TAccountsDataSource.FetchAll(const AContainer : TList<TAccount>);
 procedure TAccountsDataSource.FetchAll(const AContainer : TList<TAccount>);
 var
 var
-  i,j : integer;
+  i : integer;
   acc : TAccount;
   acc : TAccount;
   safeBox : TPCSafeBox;
   safeBox : TPCSafeBox;
-  GC : TDisposables;
-  left,right:TAccountKey;
 begin
 begin
-  FLastOverview.TotalPASC := 0;
-  FLastOverview.TotalPASA := 0;
-  safeBox := TUserInterface.Node.Bank.SafeBox;
+  safeBox := TNode.Node.Bank.SafeBox;
   safeBox.StartThreadSafe;
   safeBox.StartThreadSafe;
   try
   try
    if FKeys.Count = 0 then
    if FKeys.Count = 0 then
-     for i := 0 to safeBox.AccountsCount - 1 do begin
-       // Load all accounts
-       AContainer.Add(safeBox.Account(i));
-       FLastOverview.TotalPASC := FLastOverview.TotalPASC + acc.Balance;
-       inc(FLastOverview.TotalPASA);
-     end
-   else begin
-     // load key-matching accounts
-     for i := 0 to safeBox.AccountsCount - 1 do begin
+     for i := 0 to safeBox.AccountsCount - 1 do
+       AContainer.Add(safeBox.Account(i)) // Load all accounts
+   else
+     for i := 0 to safeBox.AccountsCount - 1 do begin // Load key-matching accounts
        acc := safeBox.Account(i);
        acc := safeBox.Account(i);
-       if FKeys.Contains(acc.accountInfo.accountKey) then begin
-         AContainer.Add(acc);
-         FLastOverview.TotalPASC := FLastOverview.TotalPASC + acc.Balance;
-         inc(FLastOverview.TotalPASA);
-       end else begin
-         for left in FKeys do begin
-           right := acc.accountInfo.accountKey;
-
-         end;
-       end;
+       if FKeys.Contains(acc.accountInfo.accountKey) then
+         AContainer.Add(acc)
      end;
      end;
-   end;
   finally
   finally
    safeBox.EndThreadSave;
    safeBox.EndThreadSave;
   end;
   end;
-  FLastKnownUserAccounts := AContainer.ToArray;
+end;
+
+{ TFastAccountsDataSource }
+
+constructor TFastAccountsDataSource.Create(AOwner: TComponent; APSummary : PUserSummary);
+begin
+  inherited Create(AOwner);
+  FPSummary := APSummary;
+end;
+
+procedure TFastAccountsDataSource.FetchAll(const AContainer : TList<TAccount>);
+var
+  i : integer;
+begin
+  if NOT Assigned(FPSummary) then exit;
+  for i := Low(FPSummary^.Accounts) to High(FPSummary^.Accounts) do
+    if FKeys.Contains(FPSummary^.Accounts[i].accountInfo.accountKey) then
+      AContainer.Add(FPSummary^.Accounts[i]);
 end;
 end;
 
 
 { TOperationsDataSourceBase }
 { TOperationsDataSourceBase }
@@ -283,7 +285,7 @@ begin
   else if ABindingName = 'BlockLocation' then
   else if ABindingName = 'BlockLocation' then
     Result := IIF(AItem.OpType <> CT_PseudoOp_Reward, Inttostr(AItem.Block) + '/' + Inttostr(AItem.NOpInsideBlock+1), Inttostr(AItem.Block))
     Result := IIF(AItem.OpType <> CT_PseudoOp_Reward, Inttostr(AItem.Block) + '/' + Inttostr(AItem.NOpInsideBlock+1), Inttostr(AItem.Block))
   else if ABindingName = 'BlockLocationSortable' then
   else if ABindingName = 'BlockLocationSortable' then
-    Result := UInt64(AItem.Block) * 4294967296 + UInt32(AItem.NOpInsideBlock)   // number pattern = [block][opindex]
+    Result := IIF(AItem.OpType <> CT_PseudoOp_Reward, UInt64(AItem.Block) * 4294967296 + UInt32(AItem.NOpInsideBlock), 0)  // number pattern = [block][opindex]
   else if ABindingName = 'Account' then
   else if ABindingName = 'Account' then
     Result := TAccountComp.AccountNumberToAccountTxtNumber(AItem.AffectedAccount)
     Result := TAccountComp.AccountNumberToAccountTxtNumber(AItem.AffectedAccount)
   else if ABindingName = 'AccountNumber' then
   else if ABindingName = 'AccountNumber' then
@@ -352,7 +354,7 @@ begin
   node := TNode.Node;
   node := TNode.Node;
   if Not Assigned(Node)
   if Not Assigned(Node)
     then exit;
     then exit;
-  TUserInterface.Node.Bank.SafeBox.StartThreadSafe;
+  TNode.Node.Bank.SafeBox.StartThreadSafe;
   try
   try
     accountBlockOps := GC.AddObject(TOperationsResumeList.Create ) as TOperationsResumeList;
     accountBlockOps := GC.AddObject(TOperationsResumeList.Create ) as TOperationsResumeList;
     list := GC.AddObject( Classes.TList.Create ) as Classes.TList;
     list := GC.AddObject( Classes.TList.Create ) as Classes.TList;
@@ -378,7 +380,7 @@ begin
         AContainer.Add(accountBlockOps[i]);
         AContainer.Add(accountBlockOps[i]);
     end;
     end;
   finally
   finally
-   TUserInterface.Node.Bank.SafeBox.EndThreadSave;
+   TNode.Node.Bank.SafeBox.EndThreadSave;
   end;
   end;
 end;
 end;
 
 

+ 133 - 74
src/gui/UCTRLWallet.pas

@@ -9,7 +9,7 @@ interface
 uses
 uses
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Menus,
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Menus,
   ExtCtrls, PairSplitter, Buttons, UVisualGrid, UCommon.UI, Generics.Collections,
   ExtCtrls, PairSplitter, Buttons, UVisualGrid, UCommon.UI, Generics.Collections,
-  UAccounts, UDataSources, UNode, UWIZSendPASC, UWIZChangeKey;
+  UAccounts, UDataSources, UNode, UWIZSendPASC, UWIZChangeKey, UCoreObjects, UCoreUtils;
 
 
 type
 type
 
 
@@ -17,7 +17,7 @@ type
 
 
   TCTRLWalletAccountsMode = (wamMyAccounts, wamFirstAccount);
   TCTRLWalletAccountsMode = (wamMyAccounts, wamFirstAccount);
   TCTRLWalletOperationsMode = (womSelectedAccounts, womAllAccounts);
   TCTRLWalletOperationsMode = (womSelectedAccounts, womAllAccounts);
-  TCTRLWalletOperationsHistory = (woh30Days, wohFullHistory);
+  TCTRLWalletOperationsHistory = (woh7Days, woh30Days, wohFullHistory);
 
 
   TCTRLWallet = class(TApplicationForm)
   TCTRLWallet = class(TApplicationForm)
     cbAccounts: TComboBox;
     cbAccounts: TComboBox;
@@ -65,20 +65,23 @@ type
     FOperationsHistory: TCTRLWalletOperationsHistory;
     FOperationsHistory: TCTRLWalletOperationsHistory;
     FAccountsGrid: TVisualGrid;
     FAccountsGrid: TVisualGrid;
     FOperationsGrid: TVisualGrid;
     FOperationsGrid: TVisualGrid;
-    FAccountsDataSource: TAccountsDataSource;
+    FUserData : TUserSummary;
+    FAccountsDataSource: TFastAccountsDataSource;
     FOperationsDataSource: TAccountsOperationsDataSource;
     FOperationsDataSource: TAccountsOperationsDataSource;
     procedure SetAccountsMode(AMode: TCTRLWalletAccountsMode);
     procedure SetAccountsMode(AMode: TCTRLWalletAccountsMode);
     procedure SetOperationsMode(AMode: TCTRLWalletOperationsMode);
     procedure SetOperationsMode(AMode: TCTRLWalletOperationsMode);
     procedure SetOperationsHistory(AHistory: TCTRLWalletOperationsHistory);
     procedure SetOperationsHistory(AHistory: TCTRLWalletOperationsHistory);
+    procedure FetchUserData;
     procedure RefreshMyAccountsCombo;
     procedure RefreshMyAccountsCombo;
-    function GetAccNoWithoutChecksum(constref ARow: variant): cardinal; inline;
+    procedure RefreshTotals;
     function GetAccounts(const AccountNumbers: TArray<cardinal>): TArray<TAccount>;
     function GetAccounts(const AccountNumbers: TArray<cardinal>): TArray<TAccount>;
   protected
   protected
     procedure ActivateFirstTime; override;
     procedure ActivateFirstTime; override;
+    function HasUserDataChanged : boolean; overload;
+    function HasUserDataChanged(out AChangedAccounts : TArray<TAccount>) : boolean; overload;
     procedure OnPrivateKeysChanged(Sender: TObject);
     procedure OnPrivateKeysChanged(Sender: TObject);
     procedure OnNodeBlocksChanged(Sender: TObject);
     procedure OnNodeBlocksChanged(Sender: TObject);
     procedure OnNodeNewOperation(Sender: TObject);
     procedure OnNodeNewOperation(Sender: TObject);
-    procedure OnAccountsUpdated(Sender: TObject);
     procedure OnAccountsSelected(Sender: TObject; constref ASelection: TVisualGridSelection);
     procedure OnAccountsSelected(Sender: TObject; constref ASelection: TVisualGridSelection);
     procedure OnOperationSelected(Sender: TObject; constref ASelection: TVisualGridSelection);
     procedure OnOperationSelected(Sender: TObject; constref ASelection: TVisualGridSelection);
     procedure OnPrepareAccountPopupMenu(Sender: TObject; constref ASelection: TVisualGridSelection; out APopupMenu: TPopupMenu);
     procedure OnPrepareAccountPopupMenu(Sender: TObject; constref ASelection: TVisualGridSelection; out APopupMenu: TPopupMenu);
@@ -92,7 +95,7 @@ type
 implementation
 implementation
 
 
 uses
 uses
-  UUserInterface, UCellRenderers, UBlockChain, UWallet, UCrypto, UCoreUtils,
+  UUserInterface, UCellRenderers, UBlockChain, UWallet, UCrypto,
   UCommon, UMemory, Generics.Defaults, UCommon.Data, UCommon.Collections, UWIZModels;
   UCommon, UMemory, Generics.Defaults, UCommon.Data, UCommon.Collections, UWIZModels;
 
 
 {$R *.lfm}
 {$R *.lfm}
@@ -109,19 +112,21 @@ begin
   FNodeNotifyEvents.OnOperationsChanged := OnNodeNewOperation;
   FNodeNotifyEvents.OnOperationsChanged := OnNodeNewOperation;
   TWallet.Keys.OnChanged.Add(OnPrivateKeysChanged);
   TWallet.Keys.OnChanged.Add(OnPrivateKeysChanged);
 
 
-
   // fields
   // fields
-  FAccountsDataSource := TAccountsDataSource.Create(Self);
-  FAccountsDataSource.FilterKeys := TWallet.Keys.AccountsKeyList.ToArray;
+  FAccountsDataSource := TFastAccountsDataSource.Create(Self);
+  FAccountsDataSource.Summary:= @FUserData;
   FOperationsDataSource := TAccountsOperationsDataSource.Create(Self);
   FOperationsDataSource := TAccountsOperationsDataSource.Create(Self);
+  FOperationsHistory := woh7Days;
+  FOperationsMode:= womAllAccounts;
+  FAccountsMode := wamMyAccounts;
 
 
   // grids
   // grids
   FAccountsGrid := TVisualGrid.Create(Self);
   FAccountsGrid := TVisualGrid.Create(Self);
   FAccountsGrid.SortMode := smMultiColumn;
   FAccountsGrid.SortMode := smMultiColumn;
-  FAccountsGrid.FetchDataInThread := True;
+  FAccountsGrid.FetchDataInThread := true;
   FAccountsGrid.AutoPageSize := True;
   FAccountsGrid.AutoPageSize := True;
-  FAccountsGrid.SelectionType := stMultiRow;
   FAccountsGrid.DeselectionType := dtDefault;
   FAccountsGrid.DeselectionType := dtDefault;
+  FAccountsGrid.SelectionType := stMultiRow;
   FAccountsGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone, vgoAutoHidePaging, vgoAutoHideSearchPanel];
   FAccountsGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone, vgoAutoHidePaging, vgoAutoHideSearchPanel];
   with FAccountsGrid.AddColumn('Account') do
   with FAccountsGrid.AddColumn('Account') do
   begin
   begin
@@ -152,15 +157,14 @@ begin
   end;
   end;
 
 
   FAccountsGrid.OnSelection := OnAccountsSelected;
   FAccountsGrid.OnSelection := OnAccountsSelected;
-  FAccountsGrid.OnFinishedUpdating := OnAccountsUpdated;
   FAccountsGrid.OnPreparePopupMenu := OnPrepareAccountPopupMenu;
   FAccountsGrid.OnPreparePopupMenu := OnPrepareAccountPopupMenu;
 
 
   FOperationsGrid := TVisualGrid.Create(Self);
   FOperationsGrid := TVisualGrid.Create(Self);
   FOperationsGrid.SortMode := smMultiColumn;
   FOperationsGrid.SortMode := smMultiColumn;
   FOperationsGrid.FetchDataInThread := True;
   FOperationsGrid.FetchDataInThread := True;
   FOperationsGrid.AutoPageSize := True;
   FOperationsGrid.AutoPageSize := True;
-  FOperationsGrid.SelectionType := stRow;
   FOperationsGrid.DeselectionType := dtDefault;
   FOperationsGrid.DeselectionType := dtDefault;
+  FOperationsGrid.SelectionType := stRow;
   FOperationsGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone, vgoAutoHidePaging, vgoAutoHideSearchPanel];
   FOperationsGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone, vgoAutoHidePaging, vgoAutoHideSearchPanel];
   with FOperationsGrid.AddColumn('Time') do
   with FOperationsGrid.AddColumn('Time') do
   begin
   begin
@@ -253,6 +257,7 @@ begin
   cmbDuration.ReadOnly := True;
   cmbDuration.ReadOnly := True;
   cmbDuration.Items.BeginUpdate;
   cmbDuration.Items.BeginUpdate;
   try
   try
+    cmbDuration.AddItem('7 Days', TObject(woh7Days));
     cmbDuration.AddItem('30 Days', TObject(woh30Days));
     cmbDuration.AddItem('30 Days', TObject(woh30Days));
     cmbDuration.AddItem('Maximum', TObject(wohFullHistory));
     cmbDuration.AddItem('Maximum', TObject(wohFullHistory));
   finally
   finally
@@ -262,6 +267,13 @@ begin
   cmbDuration.OnChange := cmbDurationChange;
   cmbDuration.OnChange := cmbDurationChange;
   FOperationsGrid.WidgetControl := cmbDuration;
   FOperationsGrid.WidgetControl := cmbDuration;
 
 
+  // Add datasources to grid
+  FAccountsGrid.DataSource := FAccountsDataSource;
+  FOperationsGrid.DataSource := FOperationsDataSource;
+
+  // Add grid to panels
+  paAccounts.AddControlDockCenter(FAccountsGrid);
+  paOperations.AddControlDockCenter(FOperationsGrid);
 end;
 end;
 
 
 procedure TCTRLWallet.FormDestroy(Sender: TObject);
 procedure TCTRLWallet.FormDestroy(Sender: TObject);
@@ -277,14 +289,42 @@ end;
 
 
 procedure TCTRLWallet.ActivateFirstTime;
 procedure TCTRLWallet.ActivateFirstTime;
 begin
 begin
-  // Configure grid states
-  AccountsMode := wamMyAccounts;
-  OperationsMode := womAllAccounts;
-  OperationsHistory := woh30Days;
-
-  // Load up selected for some reasons
-  FAccountsGrid.InternalDrawGrid.ClearSelections;
-  FOperationsGrid.InternalDrawGrid.ClearSelections;
+  FetchUserData;
+  RefreshTotals;
+  FAccountsGrid.RefreshGrid;
+  FOperationsGrid.RefreshGrid;
+end;
+
+procedure TCTRLWallet.FetchUserData;
+var
+  GC : TDisposables;
+
+  function GetAccNo(constref AAccount: TAccount): cardinal; overload;
+  begin
+    Result := AAccount.account;
+  end;
+
+begin
+  FUserData := TUserInterface.Node.Bank.SafeBox.GetUserSummary(TWallet.Keys.AccountsKeyList.ToArray, true);
+  FOperationsDataSource.Accounts := TListTool<TAccount, cardinal>.Transform(FUserData.Accounts, GetAccNo);
+end;
+
+function TCTRLWallet.HasUserDataChanged : boolean;
+var LArr : TArray<TAccount>;
+begin
+  Result := HasUserDataChanged(LArr);
+end;
+
+function TCTRLWallet.HasUserDataChanged(out AChangedAccounts : TArray<TAccount>) : boolean;
+begin
+  AChangedAccounts := TUserInterface.Node.Bank.SafeBox.GetModifiedAccounts(FUserData.Accounts);
+  Result := Length (AChangedAccounts) > 0;
+end;
+
+procedure TCTRLWallet.RefreshTotals;
+begin
+  lblTotalPASC.Caption := TAccountComp.FormatMoney(FUserData.TotalPASC);
+  lblTotalPASA.Caption := Format('%d', [FUserData.TotalPASA]);
 end;
 end;
 
 
 procedure TCTRLWallet.RefreshMyAccountsCombo;
 procedure TCTRLWallet.RefreshMyAccountsCombo;
@@ -384,27 +424,31 @@ begin
 end;
 end;
 
 
 procedure TCTRLWallet.SetAccountsMode(AMode: TCTRLWalletAccountsMode);
 procedure TCTRLWallet.SetAccountsMode(AMode: TCTRLWalletAccountsMode);
-var
-  sel1: TVisualGridSelection;
-  sel2: TRect;
 begin
 begin
-  FAccountsMode := AMode;
-  paAccounts.RemoveAllControls(False);
-  case AMode of
-    wamMyAccounts:
-    begin
-      FOperationsGrid.DataSource := FOperationsDataSource;
-      FAccountsGrid.DataSource := FAccountsDataSource;
-      FAccountsGrid.Caption.Text := 'My Accounts';
-      paAccounts.RemoveAllControls(False);
-      sel1 := FAccountsGrid.Selection;
-      sel2 := FAccountsGrid.InternalDrawGrid.Selection;
-      paAccounts.RemoveAllControls(False);
-      paAccounts.AddControlDockCenter(FAccountsGrid);
-      paOperations.RemoveAllControls(False);
-      paOperations.AddControlDockCenter(FOperationsGrid);
+  FUILock.Acquire;
+  try
+    FAccountsMode := AMode;
+    paAccounts.RemoveAllControls(False);
+    case AMode of
+      wamMyAccounts:
+      begin
+        // reset account combo
+        cbAccounts.OnChange := nil; // disable event
+        cbAccounts.ItemIndex :=0;
+        cbAccounts.OnChange := cbAccountsChange; // re-enable event
+
+        // reset accounts grid/datasource
+        FAccountsGrid.DataSource := FAccountsDataSource;
+        FAccountsGrid.Caption.Text := 'My Accounts';
+        if FAccountsGrid.Parent <> paAccounts then begin
+          paAccounts.RemoveAllControls(False);
+          paAccounts.AddControlDockCenter(FAccountsGrid);
+        end;
+      end;
+      wamFirstAccount: raise Exception.Create('Not implemented');
     end;
     end;
-    wamFirstAccount: raise Exception.Create('Not implemented');
+  finally
+    FUILock.Release;
   end;
   end;
 end;
 end;
 
 
@@ -421,32 +465,34 @@ procedure TCTRLWallet.SetOperationsMode(AMode: TCTRLWalletOperationsMode);
   end;
   end;
 
 
 begin
 begin
-  case AMode of
-    womAllAccounts:
-    begin
-      FOperationsGrid.Caption.Text := '';
-      FOperationsDataSource.Accounts :=
-        TListTool<TAccount, cardinal>.Transform(
-        FAccountsDataSource.LastFetchResult, GetAccNo);
+  if AMode = FOperationsMode then
+    exit;
+  FUILock.Acquire;
+  try
+    FOperationsMode := AMode;
+    case AMode of
+      womAllAccounts: begin
+        FOperationsGrid.Caption.Text := '';
+        FOperationsDataSource.Accounts := TListTool<TAccount, cardinal>.Transform(FUserData.Accounts, GetAccNo);
+      end;
+      womSelectedAccounts:
+      begin
+        FOperationsGrid.Caption.Text := 'Selected Accounts';
+        FOperationsDataSource.Accounts := TListTool<variant, cardinal>.Transform(FAccountsGrid.SelectedRows, GetAccNo);
+      end else
+        raise ENotSupportedException.Create(Format('AMode %d not supported', [integer(AMode)]));
     end;
     end;
-    womSelectedAccounts:
-    begin
-      FOperationsGrid.Caption.Text := 'Selected Accounts';
-      FOperationsDataSource.Accounts :=
-        TListTool<variant, cardinal>.Transform(FAccountsGrid.SelectedRows, GetAccNo);
-    end
-    else
-      raise ENotSupportedException.Create(Format('AMode %d not supported',
-        [integer(AMode)]));
+    FOperationsGrid.RefreshGrid;
+  finally
+    FUILock.Release;
   end;
   end;
-  FOperationsGrid.RefreshGrid;
-  FOperationsMode := AMode;
 end;
 end;
 
 
 procedure TCTRLWallet.SetOperationsHistory(AHistory: TCTRLWalletOperationsHistory);
 procedure TCTRLWallet.SetOperationsHistory(AHistory: TCTRLWalletOperationsHistory);
 begin
 begin
   FOperationsHistory := AHistory;
   FOperationsHistory := AHistory;
   case FOperationsHistory of
   case FOperationsHistory of
+    woh7Days: FOperationsDataSource.TimeSpan := TTimeSpan.FromDays(7);
     woh30Days: FOperationsDataSource.TimeSpan := TTimeSpan.FromDays(30);
     woh30Days: FOperationsDataSource.TimeSpan := TTimeSpan.FromDays(30);
     wohFullHistory: FOperationsDataSource.TimeSpan := TTimeSpan.FromDays(10 * 365);
     wohFullHistory: FOperationsDataSource.TimeSpan := TTimeSpan.FromDays(10 * 365);
   end;
   end;
@@ -459,23 +505,28 @@ begin
 end;
 end;
 
 
 procedure TCTRLWallet.OnNodeBlocksChanged(Sender: TObject);
 procedure TCTRLWallet.OnNodeBlocksChanged(Sender: TObject);
+var changed : TArray<TAccount>;
 begin
 begin
+  if NOT TUserInterface.Node.HasBestKnownBlockchainTip then
+    exit; // node syncing
+
+  FetchUserData;
+  RefreshTotals;
   FAccountsGrid.RefreshGrid;
   FAccountsGrid.RefreshGrid;
   FOperationsGrid.RefreshGrid;
   FOperationsGrid.RefreshGrid;
 end;
 end;
 
 
 procedure TCTRLWallet.OnNodeNewOperation(Sender: TObject);
 procedure TCTRLWallet.OnNodeNewOperation(Sender: TObject);
 begin
 begin
+  if (NOT TUserInterface.Node.HasBestKnownBlockchainTip) OR (NOT HasUserDataChanged) then
+    exit; // node syncing
+
+  FetchUserData;
+  RefreshTotals;
   FAccountsGrid.RefreshGrid;
   FAccountsGrid.RefreshGrid;
   FOperationsGrid.RefreshGrid;
   FOperationsGrid.RefreshGrid;
 end;
 end;
 
 
-procedure TCTRLWallet.OnAccountsUpdated(Sender: TObject);
-begin
-  lblTotalPASC.Caption := TAccountComp.FormatMoney(FAccountsDataSource.Overview.TotalPASC);
-  lblTotalPASA.Caption := Format('%d', [FAccountsDataSource.Overview.TotalPASA]);
-end;
-
 procedure TCTRLWallet.OnAccountsSelected(Sender: TObject; constref ASelection: TVisualGridSelection);
 procedure TCTRLWallet.OnAccountsSelected(Sender: TObject; constref ASelection: TVisualGridSelection);
 var
 var
   row: longint;
   row: longint;
@@ -507,8 +558,10 @@ begin
   if (row >= 0) and (row < FOperationsGrid.RowCount) then begin
   if (row >= 0) and (row < FOperationsGrid.RowCount) then begin
     v := FOperationsGrid.Rows[row];
     v := FOperationsGrid.Rows[row];
     ophash := FOperationsGrid.Rows[row].OPHASH;
     ophash := FOperationsGrid.Rows[row].OPHASH;
-    if TPCOperation.IsValidOperationHash(ophash) then
+    if TPCOperation.IsValidOperationHash(ophash) then begin
       TUserInterface.ShowOperationInfoDialog(self, ophash);
       TUserInterface.ShowOperationInfoDialog(self, ophash);
+      FOperationsGrid.ClearSelection;
+    end;
   end;
   end;
 end;
 end;
 
 
@@ -543,8 +596,9 @@ begin
     exit;
     exit;
 
 
   case cmbDuration.ItemIndex of
   case cmbDuration.ItemIndex of
-    0: OperationsHistory := woh30Days;
-    1: OperationsHistory := wohFullHistory;
+    0: OperationsHistory := woh7Days;
+    1: OperationsHistory := woh30Days;
+    2: OperationsHistory := wohFullHistory;
   end;
   end;
 end;
 end;
 
 
@@ -581,6 +635,12 @@ var
   wiz: TWIZSendPASCWizard;
   wiz: TWIZSendPASCWizard;
   model: TWIZOperationsModel;
   model: TWIZOperationsModel;
   AccountNumbersWithoutChecksum: TArray<cardinal>;
   AccountNumbersWithoutChecksum: TArray<cardinal>;
+
+  function GetAccNoWithoutChecksum(constref ARow: variant): cardinal;
+  begin
+    Result := ARow.__KEY;
+  end;
+
 begin
 begin
   wiz := Scoped.AddObject(TWIZSendPASCWizard.Create(nil)) as TWIZSendPASCWizard;
   wiz := Scoped.AddObject(TWIZSendPASCWizard.Create(nil)) as TWIZSendPASCWizard;
   model := TWIZOperationsModel.Create(wiz, omtSendPasc);
   model := TWIZOperationsModel.Create(wiz, omtSendPasc);
@@ -595,6 +655,12 @@ var
   wiz: TWIZChangeKeyWizard;
   wiz: TWIZChangeKeyWizard;
   model: TWIZOperationsModel;
   model: TWIZOperationsModel;
   AccountNumbersWithoutChecksum: TArray<cardinal>;
   AccountNumbersWithoutChecksum: TArray<cardinal>;
+
+  function GetAccNoWithoutChecksum(constref ARow: variant): cardinal;
+  begin
+    Result := ARow.__KEY;
+  end;
+
 begin
 begin
   wiz := Scoped.AddObject(TWIZChangeKeyWizard.Create(nil)) as TWIZChangeKeyWizard;
   wiz := Scoped.AddObject(TWIZChangeKeyWizard.Create(nil)) as TWIZChangeKeyWizard;
   model := TWIZOperationsModel.Create(wiz, omtChangeKey);
   model := TWIZOperationsModel.Create(wiz, omtChangeKey);
@@ -637,11 +703,4 @@ begin
   TUserInterface.ShowOperationInfoDialog(Self, FOperationsGrid.SelectedRows[0].__KEY);
   TUserInterface.ShowOperationInfoDialog(Self, FOperationsGrid.SelectedRows[0].__KEY);
 end;
 end;
 
 
-// Aux
-
-function TCTRLWallet.GetAccNoWithoutChecksum(constref ARow: variant): cardinal;
-begin
-  Result := ARow.__KEY;
-end;
-
 end.
 end.

+ 4 - 0
src/gui/UCellRenderers.pas

@@ -175,6 +175,10 @@ begin
     end;
     end;
     CT_Op_ChangeKeySigned: result :=  'Change Key';
     CT_Op_ChangeKeySigned: result :=  'Change Key';
     CT_Op_ChangeAccountInfo: result := 'Change Info';
     CT_Op_ChangeAccountInfo: result := 'Change Info';
+    CT_Op_MultiOperation: case OpSubType of
+      CT_OpSubtype_MultiOperation_Global: Result := 'Mixed-Transfer';
+      CT_OpSubtype_MultiOperation_AccountInfo: Result := 'Mixed-Change';
+    end;
     else result := 'Unknown';
     else result := 'Unknown';
   end;
   end;
 end;
 end;

+ 6 - 2
src/pascalcoin_wallet.lpi

@@ -33,7 +33,7 @@
         <PackageName Value="LCL"/>
         <PackageName Value="LCL"/>
       </Item1>
       </Item1>
     </RequiredPackages>
     </RequiredPackages>
-    <Units Count="97">
+    <Units Count="98">
       <Unit0>
       <Unit0>
         <Filename Value="pascalcoin_wallet.dpr"/>
         <Filename Value="pascalcoin_wallet.dpr"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
@@ -346,7 +346,7 @@
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
       </Unit62>
       </Unit62>
       <Unit63>
       <Unit63>
-        <Filename Value="core.utils\UCoreUtils.pas"/>
+        <Filename Value="core.utils\UCoreObjects.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
       </Unit63>
       </Unit63>
       <Unit64>
       <Unit64>
@@ -549,6 +549,10 @@
         <Filename Value="gui-classic\UGridUtils.pas"/>
         <Filename Value="gui-classic\UGridUtils.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
       </Unit96>
       </Unit96>
+      <Unit97>
+        <Filename Value="core.utils\UCoreUtils.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit97>
     </Units>
     </Units>
   </ProjectOptions>
   </ProjectOptions>
   <CompilerOptions>
   <CompilerOptions>