Browse Source

GUI: refactor and cleanup event pattern

Herman Schoenfeld 7 years ago
parent
commit
a753c82db8

+ 2 - 0
src/gui/UCTRLSyncronization.lfm

@@ -8,6 +8,8 @@ object CTRLSyncronization: TCTRLSyncronization
   Caption = 'Synchronization'
   ClientHeight = 428
   ClientWidth = 728
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
   LCLVersion = '1.8.4.0'
   Visible = False
   object paSplash: TPanel

+ 174 - 102
src/gui/UCTRLSyncronization.pas

@@ -21,14 +21,10 @@ interface
 
 uses
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
-  StdCtrls, ComCtrls, Buttons, UCommon.UI;
+  StdCtrls, ComCtrls, Buttons, UCommon.UI, UNetProtocol, UBaseTypes;
 
 type
 
-  { TSyncMode }
-
-  TSyncMode = (smUnset, smInitialising, smReady);
-
   { TCTRLSyncronization }
 
   TCTRLSyncronization = class(TApplicationForm)
@@ -62,137 +58,217 @@ type
     paSplash: TPanel;
     paSync: TPanel;
     procedure btnBackClick(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
     procedure lblReceivedMessagesClick(Sender:TObject);
   private
     FMinedBlocksCount: Integer;
-    FMode : TSyncMode;
+    FShowSplash : boolean;
+    FMessagesUnreadCount : Integer;
+    procedure OnLoading(Sender: TObject; const message : AnsiString; curPos, totalCount : Int64);
+    procedure OnLoaded(Sender: TObject);
     procedure SetMinedBlocksCount(const Value: Integer);
-    procedure SetSyncMode(AMode : TSyncMode);
+    procedure SetShowSplash(ABool : boolean);
+    procedure SetMessagesNotificationText(const text : AnsiString);
+    function GetMessagesNotificationText : AnsiString;
+    procedure SetStatusText(AColour: TColor; AText: String);
+
+    procedure UpdateNodeStatus;
+    procedure UpdateBlockChainState;
+    procedure OnAppStarted(Sender: TObject);
+    procedure OnBlocksChanged(Sender: TObject);
+    procedure OnUIRefreshTimer(Sender: TObject);
+    procedure OnNodeMessageEvent(NetConnection: TNetConnection; MessageData: TRawBytes);
   protected
     procedure ActivateFirstTime; override;
   public
     property MinedBlocksCount : Integer read FMinedBlocksCount write SetMinedBlocksCount;
-    property SyncMode : TSyncMode read FMode write SetSyncMode;
-    procedure UpdateNodeStatus;
-    procedure UpdateBlockChainState;
-    procedure OnFinishedLoadingDatabase;
+    property ShowSplash : boolean read FShowSplash write SetShowSplash;
+    property MessagesNotificationText : AnsiString read GetMessagesNotificationText write SetMessagesNotificationText;
   end;
 
 implementation
 
 {$R *.lfm}
 
-uses UNetProtocol,UTime,UConst, UUserInterface;
+uses UCommon, UTime, UConst, UUserInterface, UAccounts, UNode;
+
+procedure TCTRLSyncronization.FormCreate(Sender: TObject);
+begin
+  TUserInterface.AppStarted.Add(OnAppStarted);
+  TUserInterface.Loading.Add(OnLoading);
+  TUserInterface.BlocksChanged.Add(OnBlocksChanged);
+  TUserInterface.UIRefreshTimer.Add(OnUIRefreshTimer);
+  TUserInterface.NodeMessageEvent.Add(OnNodeMessageEvent);
+  FMessagesUnreadCount := 0;
+end;
+
+procedure TCTRLSyncronization.FormDestroy(Sender: TObject);
+begin
+ TUserInterface.AppStarted.Remove(OnAppStarted);
+ TUserInterface.Loading.Remove (OnLoading);
+ TUserInterface.BlocksChanged.Remove(OnBlocksChanged);
+ TUserInterface.UIRefreshTimer.Remove(OnUIRefreshTimer);
+ TUserInterface.NodeMessageEvent.Remove(OnNodeMessageEvent);
+end;
 
 procedure TCTRLSyncronization.ActivateFirstTime;
 begin
-  FMode := smInitialising;
-  paSplash.Visible:= true;
-  paSync.Visible := false;
+  ShowSplash := true;
 end;
 
-procedure TCTRLSyncronization.SetSyncMode(AMode : TSyncMode);
+procedure TCTRLSyncronization.OnAppStarted(Sender: TObject);
 begin
-  if FMode = AMode then exit;
-  case AMode of
-    smInitialising: begin
-      TUserInterface.Enabled := false;
-      paSplash.Visible:= true;
-      paSync.Visible := false;
-    end;
-    smReady: begin
-      TUserInterface.Enabled := true;
-      paSplash.Visible:= false;
-      paSync.Visible := true;
-    end;
+  UpdateBlockChainState;
+end;
+
+procedure TCTRLSyncronization.OnLoading(Sender : TObject; const message : AnsiString; curPos, totalCount : Int64);
+var LPercent : String;
+begin
+    if (totalCount>0) then
+      LPercent := Format('%.1f',[curPos*100/totalCount])+'%'
+    else
+      LPercent := '';
+
+    SetStatusText(clGreen, message+' '+LPercent);
+end;
+
+procedure TCTRLSyncronization.OnLoaded(Sender: TObject);
+begin
+  btnBack.Enabled:=true;
+  TUserInterface.ShowWallet;
+end;
+
+procedure TCTRLSyncronization.OnBlocksChanged(Sender: TObject);
+begin
+  UpdateBlockChainState;
+end;
+
+procedure TCTRLSyncronization.OnUIRefreshTimer(Sender: TObject);
+begin
+  UpdateBlockChainState;
+  UpdateNodeStatus;
+end;
+
+procedure TCTRLSyncronization.OnNodeMessageEvent(NetConnection: TNetConnection; MessageData: TRawBytes);
+begin
+  inc(FMessagesUnreadCount);
+  if FMessagesUnreadCount>1 then
+    MessagesNotificationText := Format('You have received %d messages',[FMessagesUnreadCount])
+  else
+    MessagesNotificationText := 'You have received 1 message';
+end;
+
+procedure TCTRLSyncronization.SetShowSplash(ABool : boolean);
+begin
+  if ABool = FShowSplash then exit;
+  FShowSplash := ABool;
+  if FShowSplash then begin
+    TUserInterface.Enabled := false;
+    paSplash.Visible:= true;
+    paSync.Visible := false;
+  end else begin
+    TUserInterface.Enabled := true;
+    paSplash.Visible:= false;
+    paSync.Visible := true;
   end;
 end;
 
+function TCTRLSyncronization.GetMessagesNotificationText : AnsiString;
+begin
+  Result := lblReceivedMessages.Caption;
+end;
+
+procedure TCTRLSyncronization.SetMessagesNotificationText(const text : AnsiString);
+begin
+  if (text = '') then lblReceivedMessages.Visible := false;
+  lblReceivedMessages.Caption := text;
+end;
+
+
+procedure TCTRLSyncronization.SetStatusText(AColour: TColor; AText: String);
+begin
+  lblNodeStatus.Font.Color := AColour;
+  lblNodeStatus.Caption := AText;
+end;
+
 procedure TCTRLSyncronization.UpdateNodeStatus;
 Var status : AnsiString;
 begin
   if not TUserInterface.Started then exit;
-  If Not Assigned(TUserInterface.Node) then begin
-    lblNodeStatus.Font.Color := clRed;
-    lblNodeStatus.Caption := 'Initializing...';
-  end else begin
-    SyncMode:=smReady;
-    If TUserInterface.Node.IsReady(status) then begin
-      if TNetData.NetData.NetStatistics.ActiveConnections>0 then begin
-        lblNodeStatus.Font.Color := clGreen;
-        if TNetData.NetData.IsDiscoveringServers then begin
-          lblNodeStatus.Caption := 'Discovering servers';
-        end else if TNetData.NetData.IsGettingNewBlockChainFromClient then begin
-          lblNodeStatus.Caption := 'Obtaining new blockchain';
-        end else begin
-          lblNodeStatus.Caption := 'Running';
-        end;
-      end else begin
-        lblNodeStatus.Font.Color := clRed;
-        lblNodeStatus.Caption := 'Alone in the world...';
-      end;
-    end else begin
-      lblNodeStatus.Font.Color := clRed;
-      lblNodeStatus.Caption := status;
-    end;
+
+  // State text
+  case TUserInterface.State of
+     uisLoading: ShowSplash := false; // text set by OnLoading
+     uisLoaded: SetStatusText(clGreen, 'Loaded');
+     uisDiscoveringPeers: SetStatusText(clGreen, 'Discovering Peers');
+     uisSyncronizingBlockchain: SetStatusText(clGreen, 'Syncronizing');
+     uisActive: SetStatusText(clGreen, 'Active');
+     uisIsolated: SetStatusText(clRed, 'Isolated');
+     uisError: SetStatusText(clRed, Format('Error: %s', [TUserInterface.StateText]));
   end;
+
+  // Protocol labels
   lblProtocolVersion.Caption := Format('%d (%d)', [TUserInterface.Node.Bank.SafeBox.CurrentProtocol,CT_BlockChain_Protocol_Available]);
   lblNetProtocolVersion.Caption := Format('%d (%d)', [CT_NetProtocol_Version, CT_NetProtocol_Available]);
-  if NOT btnBack.Enabled then begin
-    lblNodeStatus.Caption := 'Please wait until finished - ' + lblNodeStatus.Caption;
-  end;
 end;
 
 procedure TCTRLSyncronization.UpdateBlockChainState;
 Var
   f, favg : real;
 begin
-  if not TUserInterface.Started then exit;
-  UpdateNodeStatus;
-  if Assigned(TUserInterface.Node) then begin
-    if TUserInterface.Node.Bank.BlocksCount>0 then begin
-      lblTotalBlocksValue.Caption :=  Inttostr(TUserInterface.Node.Bank.BlocksCount)+' (0..'+Inttostr(TUserInterface.Node.Bank.BlocksCount-1)+')'; ;
-    end else lblTotalBlocksValue.Caption :=  '(none)';
-    lblTotalAccountsValue.Caption := Inttostr(TUserInterface.Node.Bank.AccountsCount);
-    lblBlockAgeValue.Caption := UnixTimeToLocalElapsedTime(TUserInterface.Node.Bank.LastOperationBlock.timestamp);
-    lblPendingOperationsValue.Caption := Inttostr(TUserInterface.Node.Operations.Count);
-    lblBlockTargetValue.Caption := InttoHex(TUserInterface.Node.Operations.OperationBlock.compact_target,8);
-    favg := TUserInterface.Node.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage);
-    f := (CT_NewLineSecondsAvg - favg) / CT_NewLineSecondsAvg;
-    lblTimeAverage.Caption := 'Last '+Inttostr(CT_CalcNewTargetBlocksAverage)+': '+FormatFloat('0.0',favg)+' sec. (Optimal '+Inttostr(CT_NewLineSecondsAvg)+'s) Deviation '+FormatFloat('0.00%',f*100);
-    if favg>=CT_NewLineSecondsAvg then begin
-      lblTimeAverage.Font.Color := clNavy;
+  TUserInterface.Node.Operations.Lock;
+  try
+    if not TUserInterface.Started then exit;
+    UpdateNodeStatus;
+    if Assigned(TUserInterface.Node) then begin
+      if TUserInterface.Node.Bank.BlocksCount>0 then begin
+        lblTotalBlocksValue.Caption :=  Inttostr(TUserInterface.Node.Bank.BlocksCount)+' (0..'+Inttostr(TUserInterface.Node.Bank.BlocksCount-1)+')'; ;
+      end else lblTotalBlocksValue.Caption :=  '(none)';
+      lblTotalAccountsValue.Caption := Inttostr(TUserInterface.Node.Bank.AccountsCount);
+      lblBlockAgeValue.Caption := UnixTimeToLocalElapsedTime(TUserInterface.Node.Bank.LastOperationBlock.timestamp);
+      lblPendingOperationsValue.Caption := Inttostr(TUserInterface.Node.Operations.Count);
+      lblBlockTargetValue.Caption := InttoHex(TUserInterface.Node.Operations.OperationBlock.compact_target,8);
+      favg := TUserInterface.Node.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage);
+      f := (CT_NewLineSecondsAvg - favg) / CT_NewLineSecondsAvg;
+      lblTimeAverage.Caption := 'Last '+Inttostr(CT_CalcNewTargetBlocksAverage)+': '+FormatFloat('0.0',favg)+' sec. (Optimal '+Inttostr(CT_NewLineSecondsAvg)+'s) Deviation '+FormatFloat('0.00%',f*100);
+      if favg>=CT_NewLineSecondsAvg then begin
+        lblTimeAverage.Font.Color := clNavy;
+      end else begin
+        lblTimeAverage.Font.Color := clOlive;
+      end;
+      lblTimeAverageAux.Caption := Format('Last %d: %s sec. - %d: %s sec. - %d: %s sec. - %d: %s sec. - %d: %s sec.',[
+          CT_CalcNewTargetBlocksAverage * 2 ,FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage * 2)),
+          ((CT_CalcNewTargetBlocksAverage * 3) DIV 2) ,FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage((CT_CalcNewTargetBlocksAverage * 3) DIV 2)),
+          ((CT_CalcNewTargetBlocksAverage DIV 4)*3),FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage(((CT_CalcNewTargetBlocksAverage DIV 4)*3))),
+          CT_CalcNewTargetBlocksAverage DIV 2,FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage DIV 2)),
+          CT_CalcNewTargetBlocksAverage DIV 4,FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage DIV 4))]);
     end else begin
-      lblTimeAverage.Font.Color := clOlive;
+      lblTotalBlocksValue.Caption := '';
+      lblTotalAccountsValue.Caption := '';
+      lblBlockAgeValue.Caption := '';
+      lblPendingOperationsValue.Caption := '';
+      lblBlockTargetValue.Caption := '';
+      lblTimeAverage.Caption := '';
+      lblTimeAverageAux.Caption := '';
     end;
-    lblTimeAverageAux.Caption := Format('Last %d: %s sec. - %d: %s sec. - %d: %s sec. - %d: %s sec. - %d: %s sec.',[
-        CT_CalcNewTargetBlocksAverage * 2 ,FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage * 2)),
-        ((CT_CalcNewTargetBlocksAverage * 3) DIV 2) ,FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage((CT_CalcNewTargetBlocksAverage * 3) DIV 2)),
-        ((CT_CalcNewTargetBlocksAverage DIV 4)*3),FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage(((CT_CalcNewTargetBlocksAverage DIV 4)*3))),
-        CT_CalcNewTargetBlocksAverage DIV 2,FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage DIV 2)),
-        CT_CalcNewTargetBlocksAverage DIV 4,FormatFloat('0.0',TUserInterface.Node.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage DIV 4))]);
-  end else begin
-    lblTotalBlocksValue.Caption := '';
-    lblTotalAccountsValue.Caption := '';
-    lblBlockAgeValue.Caption := '';
-    lblPendingOperationsValue.Caption := '';
-    lblBlockTargetValue.Caption := '';
-    lblTimeAverage.Caption := '';
-    lblTimeAverageAux.Caption := '';
-  end;
-  if (Assigned(TUserInterface.PoolMiningServer)) And (TUserInterface.PoolMiningServer.Active) then begin
-    If TUserInterface.PoolMiningServer.ClientsCount>0 then begin
-      lblMinersClientsValue.Caption := IntToStr(TUserInterface.PoolMiningServer.ClientsCount)+' connected JSON-RPC clients';
-      lblMinersClientsValue.Font.Color := clNavy;
+    if (Assigned(TUserInterface.PoolMiningServer)) And (TUserInterface.PoolMiningServer.Active) then begin
+      If TUserInterface.PoolMiningServer.ClientsCount>0 then begin
+        lblMinersClientsValue.Caption := IntToStr(TUserInterface.PoolMiningServer.ClientsCount)+' connected JSON-RPC clients';
+        lblMinersClientsValue.Font.Color := clNavy;
+      end else begin
+        lblMinersClientsValue.Caption := 'No JSON-RPC clients';
+        lblMinersClientsValue.Font.Color := clDkGray;
+      end;
+      MinedBlocksCount := TUserInterface.PoolMiningServer.ClientsWins;
     end else begin
-      lblMinersClientsValue.Caption := 'No JSON-RPC clients';
-      lblMinersClientsValue.Font.Color := clDkGray;
+      MinedBlocksCount := 0;
+      lblMinersClientsValue.Caption := 'JSON-RPC server not active';
+      lblMinersClientsValue.Font.Color := clRed;
     end;
-    MinedBlocksCount := TUserInterface.PoolMiningServer.ClientsWins;
-  end else begin
-    MinedBlocksCount := 0;
-    lblMinersClientsValue.Caption := 'JSON-RPC server not active';
-    lblMinersClientsValue.Font.Color := clRed;
+
+  finally
+    TUserInterface.Node.Operations.Unlock;
   end;
 end;
 
@@ -204,14 +280,10 @@ begin
   else lblBlocksFound.Font.Color := clDkGray;
 end;
 
-procedure TCTRLSyncronization.OnFinishedLoadingDatabase;
-begin
-  btnBack.Enabled:=true;
-  TUserInterface.ShowWallet;
-end;
-
 procedure TCTRLSyncronization.lblReceivedMessagesClick(Sender:TObject);
 begin
+  lblReceivedMessages.Visible := false;
+  lblReceivedMessages.Caption := text;
   TUserInterface.ShowMessagesForm;
 end;
 

+ 8 - 9
src/gui/UFRMAccountExplorer.lfm

@@ -11,11 +11,10 @@ object FRMAccountExplorer: TFRMAccountExplorer
   OnCreate = FormCreate
   OnDestroy = FormDestroy
   Position = poOwnerFormCenter
-  LCLVersion = '1.8.4.0'
   Visible = False
   object Splitter1: TSplitter
     Left = 380
-    Height = 365
+    Height = 385
     Top = 66
     Width = 5
   end
@@ -141,17 +140,17 @@ object FRMAccountExplorer: TFRMAccountExplorer
   end
   object pnlAccounts: TPanel
     Left = 0
-    Height = 365
+    Height = 385
     Top = 66
     Width = 380
     Align = alLeft
     BevelOuter = bvNone
-    ClientHeight = 365
+    ClientHeight = 385
     ClientWidth = 380
     TabOrder = 1
     object dgAccounts: TDrawGrid
       Left = 0
-      Height = 331
+      Height = 351
       Top = 0
       Width = 380
       Align = alClient
@@ -165,7 +164,7 @@ object FRMAccountExplorer: TFRMAccountExplorer
     object pnlAccountsInfo: TPanel
       Left = 0
       Height = 34
-      Top = 331
+      Top = 351
       Width = 380
       Align = alBottom
       BevelOuter = bvNone
@@ -246,7 +245,7 @@ object FRMAccountExplorer: TFRMAccountExplorer
   end
   object pcAccountsOptions: TPageControl
     Left = 385
-    Height = 365
+    Height = 385
     Top = 66
     Width = 483
     ActivePage = tsAccountOperations
@@ -255,11 +254,11 @@ object FRMAccountExplorer: TFRMAccountExplorer
     TabOrder = 2
     object tsAccountOperations: TTabSheet
       Caption = 'Operations of selected Account'
-      ClientHeight = 337
+      ClientHeight = 357
       ClientWidth = 475
       object dgAccountOperations: TDrawGrid
         Left = 0
-        Height = 337
+        Height = 357
         Top = 0
         Width = 475
         Align = alClient

+ 29 - 5
src/gui/UFRMAccountExplorer.pas

@@ -186,9 +186,6 @@ type
     procedure sbSelectedAccountsAddClick(Sender: TObject);
     procedure sbSelectedAccountsDelAllClick(Sender: TObject);
     procedure sbSelectedAccountsDelClick(Sender: TObject);
-    procedure RefreshAccountsGrid(RefreshData : Boolean);
-    procedure RefreshMyKeysCombo;
-    procedure OnAccountsSelectedGridUpdated(Sender: TObject);
   private
     { private declarations }
     FAccountsGrid : TAccountsGrid;
@@ -197,7 +194,13 @@ type
     FOrderedAccountsKeyList : TOrderedAccountKeysList;
     FMinAccountBalance : Int64;
     FMaxAccountBalance : Int64;
+    procedure RefreshMyKeysCombo;
+    procedure RefreshAccountsGrid(RefreshData : Boolean);
+    procedure OnLoaded(Sender: TObject);
+    procedure OnAccountsChanged(Sender: TObject);
+    procedure OnBlocksChanged(Sender: TObject);
     procedure OnPrivateKeysChanged(Sender: TObject);
+    procedure OnAccountsSelectedGridUpdated(Sender: TObject);
   public
     { public declarations }
     procedure OnSelectedAccountChanged;
@@ -237,15 +240,21 @@ begin
   // Get account list from SafeBox
   FOrderedAccountsKeyList := TOrderedAccountKeysList.Create(TUserInterface.Node.Bank.SafeBox,false);
 
-  // Subscribe to wallet events
+  // Subscribe to events
+  TUserInterface.Loaded.Add(OnLoaded);
   TWallet.Keys.OnChanged.Add(OnPrivateKeysChanged);
+  TUserInterface.AccountsChanged.Add(OnAccountsChanged);
+  TUserInterface.BlocksChanged.Add(OnBlocksChanged);
   Refresh;
 end;
 
 procedure TFRMAccountExplorer.FormDestroy(Sender: TObject);
 begin
-  // Unsubscribe from wallet events
+  // Unsubscribe from events
+  TUserInterface.Loaded.Remove(OnLoaded);
   TWallet.Keys.OnChanged.Remove(OnPrivateKeysChanged);
+  TUserInterface.AccountsChanged.Remove(OnAccountsChanged);
+  TUserInterface.BlocksChanged.Remove(OnBlocksChanged);
 
   // Nullify fields
   FAccountOperationsGrid.Node := Nil;
@@ -411,6 +420,21 @@ end;
 
 {%region Event Handlers: Blockchain }
 
+procedure TFRMAccountExplorer.OnLoaded(Sender: TObject);
+begin
+  RefreshAccountsGrid(true);
+end;
+
+procedure TFRMAccountExplorer.OnAccountsChanged(Sender: TObject);
+begin
+  RefreshAccountsGrid(true);
+end;
+
+procedure TFRMAccountExplorer.OnBlocksChanged(Sender: TObject);
+begin
+  RefreshAccountsGrid(false);
+end;
+
 procedure TFRMAccountExplorer.OnPrivateKeysChanged(Sender: TObject);
 begin
   Refresh;

+ 0 - 1
src/gui/UFRMExecuteOperations.lfm

@@ -8,7 +8,6 @@ object ExecuteOperations: TExecuteOperations
   ClientHeight = 307
   ClientWidth = 545
   OnCreate = FormCreate
-  LCLVersion = '1.8.4.0'
   Visible = False
   object GroupBox1: TGroupBox
     Left = 0

+ 3 - 6
src/gui/UFRMExecuteOperations.pas

@@ -29,7 +29,7 @@ type
 
   { TExecuteOperations }
 
-  TExecuteOperations = class(TApplicationFormEx)
+  TExecuteOperations = class(TApplicationForm)
     btnClose: TButton;
     GroupBox1: TGroupBox;
     paGrid: TPanel;
@@ -48,11 +48,8 @@ type
 
 implementation
 
-
-type
-
-
-  TExecuteOperationResultDataSource : class(TDataSource<
+//type
+//  TExecuteOperationResultDataSource : class(TDataSource<
 
 {$R *.lfm}
 

+ 58 - 3
src/gui/UFRMMainForm.pas

@@ -106,9 +106,15 @@ type
     procedure CM_ConnectivityChanged(var Msg: TMessage); message CM_PC_ConnectivityChanged;
     procedure CM_Terminate(var Msg: TMessage); message CM_PC_Terminate;
     procedure CM_ModeChanged(var Msg: TMessage); message CM_PC_ModeChanged;
+
+    procedure OnAppStarted(Sender: TObject);
+    procedure OnLoaded(Sender: TObject);
     procedure OnConnectivityChanged(Sender: TObject);
+    procedure OnNetStatisticsChanged(Sender: TObject);
     procedure OnWalletChanged(Sender: TObject);
+    procedure OnUIRefreshTimer(Sender: TObject);
   protected
+    procedure RefreshConnectionStatusDisplay;
     procedure RefreshWalletLockIcon;
     procedure RefreshConnectivityIcon;
     procedure ActivateFirstTime; override;
@@ -117,14 +123,13 @@ type
   public
     property SyncControl : TCTRLSyncronization read FSyncControl;
     property Mode : TFRMMainFormMode read FMode write SetMode;
-    procedure OnFinishedLoadingDatabase;
   end;
 
 implementation
 
 {$R *.lfm}
 
-uses LCLIntf, UUserInterface, UThread, UOpTransaction, UWizard;
+uses LCLIntf, UUserInterface, UThread, UOpTransaction, UWizard, UTime;
 
 const
   CT_FOOTER_TOOLBAR_LEFT_PADDING = 8;
@@ -182,16 +187,23 @@ end;
 
 procedure TFRMMainForm.ActivateFirstTime;
 begin
+  // Event subscriptions on activate, not create, due to load-time sequence
+  TUserInterface.AppStarted.Add(OnAppStarted);
   TWallet.Keys.OnChanged.Add(OnWalletChanged);
   TNetData.NetData.OnConnectivityChanged.Add(OnConnectivityChanged);
+  TUserInterface.NetStatisticsChanged.Add(OnNetStatisticsChanged);
+  TUserInterface.UIRefreshTimer.Add(OnUIRefreshTimer);
   RefreshWalletLockIcon;
   RefreshConnectivityIcon;
 end;
 
 procedure TFRMMainForm.FormDestroy(Sender: TObject);
 begin
+  TUserInterface.AppStarted.Remove(OnAppStarted);
   TWallet.Keys.OnChanged.Remove(OnWalletChanged);
   TNetData.NetData.OnConnectivityChanged.Remove(OnConnectivityChanged);
+  TUserInterface.NetStatisticsChanged.Remove(OnNetStatisticsChanged);
+  TUserInterface.UIRefreshTimer.Remove(OnUIRefreshTimer);
 end;
 
 {%endregion}
@@ -232,6 +244,23 @@ begin
   tbtnConnectivity.Hint :=  HintConst[TNetData.NetData.NetConnectionsActive];
 end;
 
+procedure TFRMMainForm.RefreshConnectionStatusDisplay;
+var errors : AnsiString;
+begin
+  //HS XXXXXXXXXXXX was triggering below handlers, ignore?
+  //FMainForm.SyncControl.UpdateNodeStatus;
+  //HandleNetStatisticsChangedBackend(FMainForm);
+  if Assigned(TUserInterface.Node) then begin
+    if TUserInterface.Node.IsBlockChainValid(errors) then begin
+      sbStatusBar.Panels[2].Text := Format('Last account time:%s', [FormatDateTime('dd/mm/yyyy hh:nn:ss',UnivDateTime2LocalDateTime(UnixToUnivDateTime( TUserInterface.Node.Bank.LastOperationBlock.timestamp )))]);
+    end else begin
+      sbStatusBar.Panels[2].Text := 'NO BLOCKCHAIN: '+errors;
+    end;
+  end else begin
+    sbStatusBar.Panels[2].Text := '';
+  end;
+end;
+
 procedure TFRMMainForm.SetMode(AMode: TFRMMainFormMode);
 var nestedForm : TForm;
 begin
@@ -284,7 +313,7 @@ begin
   Self.Enabled:=true;
 end;
 
-procedure TFRMMainForm.OnFinishedLoadingDatabase;
+procedure TFRMMainForm.OnLoaded(Sender: TObject);
 begin
   // Ensure handled in UI thread
   PostMessage(Self.Handle,CM_PC_FinishedLoadingDatabase,0,0);
@@ -306,12 +335,38 @@ begin
   RefreshConnectivityIcon;
 end;
 
+procedure TFRMMainForm.OnAppStarted(Sender: TObject);
+begin
+  RefreshConnectionStatusDisplay;
+end;
+
 procedure TFRMMainForm.OnConnectivityChanged(Sender: TObject);
 begin
   // Ensure handled in UI thread
   PostMessage(Self.Handle,CM_PC_ConnectivityChanged,0,0);
 end;
 
+procedure TFRMMainForm.OnNetStatisticsChanged(Sender: TObject);
+Var NS : TNetStatistics;
+begin
+  if Assigned(TUserInterface.Node) then begin
+    If TUserInterface.Node.NetServer.Active then begin
+      sbStatusBar.Panels[0].Text := 'Active (port: '+Inttostr(TUserInterface.Node.NetServer.Port)+')';
+    end else sbStatusBar.Panels[0].Text := 'Server Inactive';
+    NS := TNetData.NetData.NetStatistics;
+    sbStatusBar.Panels[1].Text  := Format('Connections: %d Clients: %d Servers: %d - Rcvd: %d kb Sent: %d kb',
+      [NS.ActiveConnections,NS.ClientsConnections, NS.ServersConnections, NS.BytesReceived DIV 1024, NS.BytesSend DIV 1024]);
+  end else begin
+    sbStatusBar.Panels[0].Text := '';
+    sbStatusBar.Panels[1].Text := '';
+  end;
+end;
+
+procedure TFRMMainForm.OnUIRefreshTimer(Sender: TObject);
+begin
+  RefreshConnectionStatusDisplay;
+end;
+
 {%endregion}
 
 {%region Handlers: Menu Items }

+ 3 - 0
src/gui/UFRMMessages.lfm

@@ -3,10 +3,13 @@ object FRMMessages: TFRMMessages
   Height = 441
   Top = 47
   Width = 874
+  ActiveControl = lbNetConnections
   Caption = 'Messages'
   ClientHeight = 441
   ClientWidth = 874
   OnActivate = FormActivate
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
   Position = poOwnerFormCenter
   LCLVersion = '1.8.4.0'
   Visible = False

+ 13 - 5
src/gui/UFRMMessages.pas

@@ -43,14 +43,15 @@ type
     memoMessageToSend: TMemo;
     procedure bbSendAMessageClick(Sender: TObject);
     procedure FormActivate(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
   private
     FMessagesUnreadCount : Integer;
     { private declarations }
     procedure UpdateAvailableConnections;
-
+    procedure OnNodeMessageEvent(NetConnection : TNetConnection; MessageData : TRawBytes);
   public
     { public declarations }
-    Procedure OnNodeMessageEvent(NetConnection : TNetConnection; MessageData : TRawBytes);
   end;
 
 implementation
@@ -64,9 +65,17 @@ uses UUserInterface, USettings;
 procedure TFRMMessages.FormActivate(Sender: TObject);
 begin
   UpdateAvailableConnections;
-  TUserInterface.MessagesNotificationText := '';
 end;
 
+procedure TFRMMessages.FormCreate(Sender: TObject);
+begin
+  TUserInterface.NodeMessageEvent.Add(OnNodeMessageEvent);
+end;
+
+procedure TFRMMessages.FormDestroy(Sender: TObject);
+begin
+  TUserInterface.NodeMessageEvent.Remove(OnNodeMessageEvent);
+end;
 
 procedure TFRMMessages.OnNodeMessageEvent(NetConnection: TNetConnection; MessageData: TRawBytes);
 Var s : String;
@@ -90,10 +99,9 @@ begin
   end else begin
     memoMessages.Lines.Add(DateTimeToStr(now)+' Internal message: '+MessageData);
   end;
-  if FMessagesUnreadCount>1 then TUserInterface.MessagesNotificationText := Format('You have received %d messages',[FMessagesUnreadCount])
-  else TUserInterface.MessagesNotificationText := 'You have received 1 message';
 end;
 
+
 procedure TFRMMessages.UpdateAvailableConnections;
 Var i : integer;
  NC : TNetConnection;

+ 2 - 1
src/gui/UFRMNodes.lfm

@@ -3,12 +3,13 @@ object FRMNodes: TFRMNodes
   Height = 444
   Top = 333
   Width = 861
+  ActiveControl = memoNetConnections
   Caption = 'Nodes'
   ClientHeight = 444
   ClientWidth = 861
   OnCreate = FormCreate
+  OnDestroy = FormDestroy
   Position = poOwnerFormCenter
-  LCLVersion = '1.8.4.0'
   Visible = False
   object Label3: TLabel
     Left = 15

+ 20 - 12
src/gui/UFRMNodes.pas

@@ -45,23 +45,22 @@ type
     memoNetConnections: TMemo;
     memoNetServers: TMemo;
     procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
   private
     { private declarations }
     procedure CM_NetConnectionUpdated(var Msg: TMessage); message CM_PC_NetConnectionUpdated;
     procedure CM_BlackListUpdated(var Msg: TMessage); message CM_PC_BlackListUpdated;
     procedure CM_NetNodeServersUpdated(var Msg: TMessage); message CM_PC_NetNodeServersUpdated;
+    procedure OnNetConnectionsUpdated(Sender: TObject);
+    procedure OnNetNodeServersUpdated(Sender: TObject);
+    procedure OnNetBlackListUpdated(Sender: TObject);
   public
     { public declarations }
-
-    // TODO - refactor this out with TNotifyManyEvent so form subscribes directly to event
-    procedure OnNetConnectionsUpdated;
-    procedure OnNetBlackListUpdated;
-    procedure OnNetNodeServersUpdated;
   end;
 
 implementation
 
-Uses UTime;
+uses UCommon, UUserInterface, UTime;
 
 {$R *.lfm}
 
@@ -69,11 +68,20 @@ Uses UTime;
 
 procedure TFRMNodes.FormCreate(Sender: TObject);
 begin
-  OnNetConnectionsUpdated;
-  OnNetNodeServersUpdated;
-  OnNetBlackListUpdated;
+  TUserInterface.NetConnectionsUpdated.Add(OnNetConnectionsUpdated);
+  TUserInterface.NetNodeServersUpdated.Add(OnNetNodeServersUpdated);
+  TUserInterface.NetBlackListUpdated.Add(OnNetBlackListUpdated);
+  OnNetConnectionsUpdated(Self);
+  OnNetNodeServersUpdated(Self);
+  OnNetBlackListUpdated(Self);
 end;
 
+procedure TFRMNodes.FormDestroy(Sender: TObject);
+begin
+  TUserInterface.NetConnectionsUpdated.Remove(OnNetConnectionsUpdated);
+  TUserInterface.NetNodeServersUpdated.Remove(OnNetNodeServersUpdated);
+  TUserInterface.NetBlackListUpdated.Remove(OnNetBlackListUpdated);
+end;
 
 procedure TFRMNodes.CM_NetConnectionUpdated(var Msg: TMessage);
 Const CT_BooleanToString : Array[Boolean] of String = ('False','True');
@@ -153,7 +161,7 @@ begin
   end;
 end;
 
-procedure TFRMNodes.OnNetConnectionsUpdated;
+procedure TFRMNodes.OnNetConnectionsUpdated(Sender: TObject);
 begin
   // Ensure handled in UI thread
   PostMessage(Self.Handle,CM_PC_NetConnectionUpdated,0,0);
@@ -208,7 +216,7 @@ begin
   end;
 end;
 
-procedure TFRMNodes.OnNetNodeServersUpdated;
+procedure TFRMNodes.OnNetNodeServersUpdated(Sender: TObject);
 begin
   // Ensure handled in UI thread
   PostMessage(Self.Handle,CM_PC_NetNodeServersUpdated,0,0);
@@ -252,7 +260,7 @@ begin
   end;
 end;
 
-procedure TFRMNodes.OnNetBlackListUpdated;
+procedure TFRMNodes.OnNetBlackListUpdated(Sender: TObject);
 begin
   // Ensure handled in UI thread
   PostMessage(Self.Handle,CM_PC_BlackListUpdated,0,0);

+ 257 - 268
src/gui/UUserInterface.pas

@@ -23,8 +23,10 @@ interface
 {$I ..\config.inc}
 
 uses
-  SysUtils, Classes, Forms, Controls, {$IFDEF WINDOWS}Windows,{$ENDIF} ExtCtrls, Dialogs, LCLType,
-  UCommon.UI, UBlockChain, UAccounts, UNode, UWallet, UConst, UFolderHelper, UGridUtils, URPC, UPoolMining,
+  SysUtils, Classes, Forms, Controls, {$IFDEF WINDOWS}Windows,{$ENDIF} ExtCtrls,
+  Dialogs, LCLType,
+  UCommon, UCommon.UI,
+  UBlockChain, UAccounts, UNode, UWallet, UConst, UFolderHelper, UGridUtils, URPC, UPoolMining,
   ULog, UThread, UNetProtocol, UCrypto,
   UFRMMainForm, UCTRLSyncronization, UFRMAccountExplorer, UFRMOperationExplorer, UFRMPendingOperations, UFRMOperation,
   UFRMLogs, UFRMMessages, UFRMNodes, UFRMBlockExplorer, UFRMWalletKeys;
@@ -34,6 +36,9 @@ type
 
   TLoadDatabaseThread = class;
 
+  { TUserInterfaceState }
+  TUserInterfaceState = (uisLoading, uisLoaded, uisDiscoveringPeers, uisSyncronizingBlockchain, uisActive, uisIsolated, uisDisconnected, uisError);
+
   { TUserInterface }
 
   TUserInterface = class
@@ -69,51 +74,83 @@ type
       FStatusBar2Text : AnsiString; static;
       FMessagesNotificationText : AnsiString; static;
       FDisplayedStartupSyncDialog : boolean; static;
-
-      // Methods
-      class procedure RefreshConnectionStatusDisplay;
+      FAppStarted : TNotifyManyEvent; static;
+      FLoading : TProgressNotifyMany; static;
+      FLoaded : TNotifyManyEvent; static;
+      FStateChanged : TNotifyManyEvent; static;
+      FSettingsChanged : TNotifyManyEvent; static;
+      FAccountsChanged : TNotifyManyEvent; static;
+      FBlocksChanged : TNotifyManyEvent; static;
+      FReceivedHelloMessage : TNotifyManyEvent; static;
+      FNodeMessageEvent : TNodeMessageManyEvent; static;
+      FNetStatisticsChanged : TNotifyManyEvent; static;
+      FNetConnectionsUpdated : TNotifyManyEvent; static;
+      FNetNodeServersUpdated : TNotifyManyEvent; static;
+      FNetBlackListUpdated : TNotifyManyEvent; static;
+      FMiningServerNewBlockFound : TNotifyManyEvent; static;
+      FUIRefreshTimer : TNotifyManyEvent; static;
+      FState : TUserInterfaceState; static;
+      FStateText : String; static;
 
       // Getters/Setters
       class function GetEnabled : boolean; static;
       class procedure SetEnabled(ABool: boolean); static;
-      class procedure SetStatusBar0Text(const text : AnsiString); static;
-      class procedure SetStatusBar1Text(const text : AnsiString); static;
-      class procedure SetStatusBar2Text(const text : AnsiString); static;
-      class procedure SetMessagesNotificationText(const text : AnsiString); static;
+      class procedure SetState(AState : TUserInterfaceState); static;
+      //TODO: remove
       class procedure SetMainFormMode(AMode: TFRMMainFormMode); static;
       class function GetMainFormMode : TFRMMainFormMode; static;
 
-      // Aux methods
-      class procedure FinishedLoadingDatabase;
-
       // Handlers
-      class procedure OnTimerUpdateStatusTimer(Sender: TObject);
-      class procedure OnSubFormDestroyed(Sender: TObject);
-
-      // Backend Handlers (TODO: refactor this out with TNotifyManyEvents)
       class procedure OnSettingsChanged(Sender: TObject);
-      class procedure OnAccountsChanged(Sender: TObject);
-      class procedure OnBlocksChanged(Sender: TObject);
+      class procedure OnLoaded(Sender: TObject);
+      class procedure OnUITimerRefresh(Sender: TObject);
       class procedure OnReceivedHelloMessage(Sender: TObject);
-      class procedure OnNodeMessageEvent(NetConnection: TNetConnection; MessageData: TRawBytes);
-      class procedure OnNetStatisticsChanged(Sender: TObject);
-      class procedure OnNetConnectionsUpdated(Sender: TObject);
-      class procedure OnNetNodeServersUpdated(Sender: TObject);
-      class procedure OnNetBlackListUpdated(Sender: TObject);
       class procedure OnMiningServerNewBlockFound(Sender: TObject);
+      class procedure OnSubFormDestroyed(Sender: TObject);
       class procedure OnTrayIconDblClick(Sender: TObject);
+
+      // Aux
+      class procedure NotifyLoadedEvent(Sender: TObject);
+      class procedure NotifyLoadingEvent(Sender: TObject; const message: AnsiString; curPos, totalCount: Int64);
+      class procedure NotifyStateChanged(Sender: TObject);
+      class procedure NotifySettingsChangedEvent(Sender: TObject);
+      class procedure NotifyAccountsChangedEvent(Sender: TObject);
+      class procedure NotifyBlocksChangedEvent(Sender: TObject);
+      class procedure NotifyReceivedHelloMessageEvent(Sender: TObject);
+      class procedure NotifyNodeMessageEventEvent(NetConnection: TNetConnection; MessageData: TRawBytes);
+      class procedure NotifyNetStatisticsChangedEvent(Sender: TObject);
+      class procedure NotifyNetConnectionsUpdatedEvent(Sender: TObject);
+      class procedure NotifyNetNodeServersUpdatedEvent(Sender: TObject);
+      class procedure NotifyNetBlackListUpdatedEvent(Sender: TObject);
+      class procedure NotifyMiningServerNewBlockFoundEvent(Sender: TObject);
+      class procedure NotifyUIRefreshTimerEvent(Sender: TObject);
     public
       // Properties
       class property Enabled : boolean read GetEnabled write SetEnabled;
       class property Started : boolean read FStarted;
+      class property State : TUserInterfaceState read FState write SetState;
+      class property StateText : string read FStateText;
       class property Node : TNode read FNode;
       class property Log : TLog read FLog;
       class property PoolMiningServer : TPoolMiningServer read FPoolMiningServer;
       class property MainFormMode : TFRMMainFormMode read GetMainFormMode write SetMainFormMode;
-      class property StatusBar0Text : AnsiString read FStatusBar0Text write SetStatusBar0Text;
-      class property StatusBar1Text : AnsiString read FStatusBar1Text write SetStatusBar1Text;
-      class property StatusBar2Text : AnsiString read FStatusBar2Text write SetStatusBar2Text;
-      class property MessagesNotificationText : AnsiString read FMessagesNotificationText write SetMessagesNotificationText;
+
+      // Events
+      class property AppStarted : TNotifyManyEvent read FAppStarted;
+      class property Loading : TProgressNotifyMany read FLoading;
+      class property Loaded : TNotifyManyEvent read FLoaded;
+      class property StateChanged : TNotifyManyEvent read FStateChanged;
+      class property SettingsChanged : TNotifyManyEvent read FSettingsChanged;
+      class property AccountsChanged : TNotifyManyEvent read FAccountsChanged;
+      class property BlocksChanged : TNotifyManyEvent read FBlocksChanged;
+      class property ReceivedHelloMessage : TNotifyManyEvent read FReceivedHelloMessage;
+      class property NodeMessageEvent : TNodeMessageManyEvent read FNodeMessageEvent;
+      class property NetStatisticsChanged : TNotifyManyEvent read FNetStatisticsChanged;
+      class property NetConnectionsUpdated : TNotifyManyEvent read FNetConnectionsUpdated;
+      class property NetNodeServersUpdated : TNotifyManyEvent read FNetNodeServersUpdated;
+      class property NetBlackListUpdated : TNotifyManyEvent read FNetBlackListUpdated;
+      class property MiningServerNewBlockFound : TNotifyManyEvent read FMiningServerNewBlockFound;
+      class property UIRefreshTimer : TNotifyManyEvent read FUIRefreshTimer;
 
       // Methods
       class procedure StartApplication(mainForm : TForm);
@@ -165,6 +202,8 @@ type
 
   TLoadDatabaseThread = Class(TPCThread)
   protected
+    procedure OnProgressNotify(sender : TObject; const message : AnsiString; curPos, totalCount : Int64);
+    procedure OnLoaded;
     procedure BCExecute; override;
   End;
 
@@ -176,7 +215,7 @@ implementation
 
 uses
   UFRMAbout, UFRMNodesIp, UFRMPascalCoinWalletConfig, UFRMPayloadDecoder, UFRMMemoText,
-  UOpenSSL, UFileStorage, UTime, UCommon, USettings, UCoreUtils, UMemory,
+  UOpenSSL, UFileStorage, UTime, USettings, UCoreUtils, UMemory,
   UWIZOperation, UWIZSendPASC, UWIZChangeKey, UWIZEnlistAccountForSale;
 
 {%region UI Lifecyle}
@@ -252,11 +291,12 @@ begin
     FNode := TNode.Node;
     FNode.NetServer.Port := TSettings.InternetServerPort;
     FNode.PeerCache := TSettings.PeerCache+';'+CT_Discover_IPs;
+    FReceivedHelloMessage.Add(OnReceivedHelloMessage);
 
     // Subscribe to Node events (TODO refactor with FNotifyEvents)
     FNodeNotifyEvents := TNodeNotifyEvents.Create(FMainForm);
-    FNodeNotifyEvents.OnBlocksChanged := OnBlocksChanged;
-    FNodeNotifyEvents.OnNodeMessageEvent :=  OnNodeMessageEvent;
+    FNodeNotifyEvents.OnBlocksChanged := NotifyBlocksChangedEvent;
+    FNodeNotifyEvents.OnNodeMessageEvent :=  NotifyNodeMessageEventEvent;
 
     // Start RPC server
     FRPCServer := TRPCServer.Create;
@@ -271,24 +311,26 @@ begin
     TFileStorage(FNode.Bank.Storage).Initialize;
 
     // Reading database
+    State := uisLoading;
+    FLoaded.Add(OnLoaded);
     TLoadDatabaseThread.Create(false).FreeOnTerminate := true;
 
     // Init
-    TNetData.NetData.OnReceivedHelloMessage := OnReceivedHelloMessage;
-    TNetData.NetData.OnStatisticsChanged := OnNetStatisticsChanged;
-    TNetData.NetData.OnNetConnectionsUpdated := OnNetConnectionsUpdated;
-    TNetData.NetData.OnNodeServersUpdated := OnNetNodeServersUpdated;
-    TNetData.NetData.OnBlackListUpdated := OnNetBlackListUpdated;
+    TNetData.NetData.OnReceivedHelloMessage := NotifyReceivedHelloMessageEvent;
+    TNetData.NetData.OnStatisticsChanged := NotifyNetStatisticsChangedEvent;
+    TNetData.NetData.OnNetConnectionsUpdated := NotifyNetConnectionsUpdatedEvent;
+    TNetData.NetData.OnNodeServersUpdated := NotifyNetNodeServersUpdatedEvent;
+    TNetData.NetData.OnBlackListUpdated := NotifyNetBlackListUpdatedEvent;
 
     // Start refresh timer
-    FTimerUpdateStatus.OnTimer := OnTimerUpdateStatusTimer;
+    FTimerUpdateStatus.OnTimer := NotifyUIRefreshTimerEvent;
     FTimerUpdateStatus.Interval := 1000;
     FTimerUpdateStatus.Enabled := true;
+    FUIRefreshTimer.Add(OnUITimerRefresh); //TODO: move to initialisation?
 
     // open the sync dialog
-    FMainForm.SyncControl.UpdateBlockChainState;   //TODO fix this work-flow
-    RefreshConnectionStatusDisplay;
     FStarted := true;
+    FAppStarted.Invoke(nil);
   Except
     On E:Exception do begin
       E.Message := 'An error occurred during initialization. Application cannot continue:'+#10+#10+E.Message+#10+#10+'Application will close...';
@@ -297,15 +339,8 @@ begin
     end;
   end;
 
-
   // Notify accounts changed
-  OnAccountsChanged(FMainForm);
-
-  // Refresh status bar since may not have been displayed
-  SetStatusBar0Text(FStatusBar0Text);
-  SetStatusBar0Text(FStatusBar1Text);
-  SetStatusBar0Text(FStatusBar2Text);
-  SetMessagesNotificationText(FMessagesNotificationText);
+  NotifyAccountsChangedEvent(FMainForm);
 
   // Show sync dialog
   ShowSyncDialog;
@@ -327,7 +362,7 @@ begin
   TLog.NewLog(ltinfo,Classname,'Quit Application - START');
   Try
     step := 'Saving Settings';
-    TSettings.OnChanged.Remove(OnSettingsChanged);
+    TSettings.OnChanged.Remove(NotifySettingsChangedEvent);
     TSettings.Save;
 
     // Destroys root form, non-modal forms and all their attached components
@@ -406,7 +441,41 @@ begin
   Application.BringToFront();
 end;
 
-class procedure TUserInterface.FinishedLoadingDatabase;
+{%endregion}
+
+{%region UI Handlers }
+
+class procedure TUserInterface.OnTrayIconDblClick(Sender: TObject);
+begin
+  RunInForeground;
+end;
+
+class procedure TUserInterface.OnSubFormDestroyed(Sender: TObject);
+begin
+  try
+    FUILock.Acquire;
+    if Sender = FAccountExplorer then
+      FAccountExplorer := nil // form free's on close
+    else if Sender = FPendingOperationForm then
+      FPendingOperationForm := nil // form free's on close
+    else if Sender = FOperationsExplorerForm then
+      FOperationsExplorerForm := nil // form free's on close
+    else if Sender = FBlockExplorerForm then
+      FBlockExplorerForm := nil // form free's on close
+    else if Sender = FLogsForm then
+      FLogsForm := nil // form free's on close
+    else if Sender = FNodesForm then
+      FNodesForm := nil // form free's on close
+    else if Sender = FMessagesForm then
+      FMessagesForm := nil
+    else
+      raise Exception.Create('Internal Error: [NotifySubFormDestroyed] encountered an unknown sub-form instance');
+  finally
+    FUILock.Release;
+  end;
+end;
+
+class procedure TUserInterface.OnLoaded(Sender: TObject);
 begin
   FPoolMiningServer := TPoolMiningServer.Create;
   FPoolMiningServer.Port := TSettings.MinerServerRpcPort;
@@ -414,11 +483,99 @@ begin
   FPoolMiningServer.MinerPayload := TSettings.MinerName;
   FNode.Operations.AccountKey := TWallet.MiningKey;
   FPoolMiningServer.Active := TSettings.MinerServerRpcActive;
-  FPoolMiningServer.OnMiningServerNewBlockFound := OnMiningServerNewBlockFound;
-  FMainForm.SyncControl.OnFinishedLoadingDatabase;
-  FMainForm.OnFinishedLoadingDatabase;
-  // Refresh UI
-  OnAccountsChanged(FMainForm);
+  FPoolMiningServer.OnMiningServerNewBlockFound := NotifyMiningServerNewBlockFoundEvent;
+  State := uisLoaded;
+end;
+
+class procedure TUserInterface.OnUITimerRefresh(Sender: Tobject);
+var
+  LActive, LDiscoveringPeers, LGettingNewBlockchain, LRemoteHasBiggerBlock, LNoConnections : boolean;
+  LState : TUserInterfaceState;
+  LLocalTip, LRemoteTip : Cardinal;
+begin
+  LState := FState;
+  LActive := FNode.NetServer.Active;
+  LDiscoveringPeers := TNetData.NetData.IsDiscoveringServers;
+  LGettingNewBlockchain := TNetData.NetData.IsGettingNewBlockChainFromClient;
+  LLocalTip := Node.Bank.BlocksCount;
+  LRemoteTip := TNetData.NetData.MaxRemoteOperationBlock.block;
+  LRemoteHasBiggerBlock := LRemoteTip > LLocalTip;
+  LNoConnections := TNetData.NetData.NetStatistics.ActiveConnections = 0;
+
+  if LActive then begin
+    if LDiscoveringPeers then
+      LState := uisDiscoveringPeers;
+
+    if LGettingNewBlockchain OR LRemoteHasBiggerBlock then
+      LState := uisSyncronizingBlockchain;
+
+    if LNoConnections then
+      LState := uisIsolated;
+
+    if (NOT LDiscoveringPeers) AND (NOT LGettingNewBlockchain) AND (NOT LRemoteHasBiggerBlock) AND (NOT LNoConnections) then
+      LState := uisActive;
+
+  end else LState := uisDisconnected;
+  State := LState;
+end;
+
+class procedure TUserInterface.OnSettingsChanged(Sender: TObject);
+Var wa : Boolean;
+  i : Integer;
+begin
+  if TSettings.SaveLogFiles then begin
+    if TSettings.SaveDebugLogs then
+      FLog.SaveTypes := CT_TLogTypes_ALL
+    else
+      FLog.SaveTypes := CT_TLogTypes_DEFAULT;
+    FLog.FileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'PascalCointWallet.log';
+  end else begin
+    FLog.SaveTypes := [];
+    FLog.FileName := '';
+  end;
+  if Assigned(FNode) then begin
+    wa := FNode.NetServer.Active;
+    FNode.NetServer.Port := TSettings.InternetServerPort;
+    FNode.NetServer.Active := wa;
+    FNode.Operations.BlockPayload := TSettings.MinerName;
+    FNode.NodeLogFilename := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'blocks.log';
+  end;
+  if Assigned(FPoolMiningServer) then begin
+    if FPoolMiningServer.Port <> TSettings.MinerServerRpcPort then begin
+      FPoolMiningServer.Active := false;
+      FPoolMiningServer.Port := TSettings.MinerServerRpcPort;
+    end;
+    FPoolMiningServer.Active :=TSettings.MinerServerRpcActive;
+    FPoolMiningServer.UpdateAccountAndPayload(TWallet.MiningKey, TSettings.MinerName);
+  end;
+  if Assigned(FRPCServer) then begin
+    FRPCServer.Active := TSettings.RpcPortEnabled;
+    FRPCServer.ValidIPs := TSettings.RpcAllowedIPs;
+  end;
+end;
+
+class procedure TUserInterface.OnReceivedHelloMessage(Sender: TObject);
+Var nsarr : TNodeServerAddressArray;
+  i : Integer;
+  s : AnsiString;
+begin
+  // Internal handler
+  // No lock required
+  //CheckMining;
+  // Update node servers Peer Cache
+  nsarr := TNetData.NetData.NodeServersAddresses.GetValidNodeServers(true,0);
+  s := '';
+  for i := low(nsarr) to High(nsarr) do begin
+    if (s<>'') then s := s+';';
+    s := s + nsarr[i].ip+':'+IntToStr( nsarr[i].port );
+  end;
+  TSettings.PeerCache := s;
+
+end;
+
+class procedure TUserInterface.OnMiningServerNewBlockFound(Sender: TObject);
+begin
+  FPoolMiningServer.MinerAccountKey := TWallet.MiningKey;
 end;
 
 {%endregion}
@@ -808,28 +965,6 @@ begin
   end;
 end;
 
-class procedure TUserInterface.RefreshConnectionStatusDisplay;
-var errors : AnsiString;
-begin
-  FUILock.Acquire;
-  Try
-    FMainForm.SyncControl.UpdateNodeStatus;
-    OnNetStatisticsChanged(FMainForm);
-    if Assigned(FNode) then begin
-      if FNode.IsBlockChainValid(errors) then begin
-        StatusBar2Text := Format('Last account time:%s',
-         [FormatDateTime('dd/mm/yyyy hh:nn:ss',UnivDateTime2LocalDateTime(UnixToUnivDateTime( FNode.Bank.LastOperationBlock.timestamp )))]);
-      end else begin
-        StatusBar2Text := 'NO BLOCKCHAIN: '+errors;
-      end;
-    end else begin
-      StatusBar2Text := '';
-    end;
-  finally
-    FUILock.Release;
-  end;
-end;
-
 {%endregion}
 
 {%region Auxillary methods}
@@ -841,7 +976,16 @@ end;
 
 class procedure TUserInterface.SetEnabled(ABool: boolean);
 begin
-  FMainForm.Enabled:=ABool;
+  if Assigned(FMainForm) then
+    FMainForm.Enabled:=ABool;
+end;
+
+class procedure TUserInterface.SetState(AState : TUserInterfaceState); static;
+begin
+  if AState = FState then
+    exit;
+  FState := AState;
+  NotifyStateChanged(nil);
 end;
 
 class procedure TUserInterface.SetMainFormMode(AMode: TFRMMainFormMode);
@@ -859,251 +1003,97 @@ begin
   Result := FMainForm.Mode;
 end;
 
-class procedure TUserInterface.SetStatusBar0Text(const text : AnsiString); static;
+class procedure TUserInterface.NotifyLoadedEvent(Sender: TObject);
 begin
-  FStatusBar0Text := text;
-  if Assigned(FMainForm) then
-    FMainForm.sbStatusBar.Panels[0].Text := FStatusBar0Text;
+  TUserInterface.FLoaded.Invoke(Sender);
 end;
 
-class procedure TUserInterface.SetStatusBar1Text(const text : AnsiString); static;
+class procedure TUserInterface.NotifyLoadingEvent(Sender: TObject; const message: AnsiString; curPos, totalCount: Int64);
 begin
-  FStatusBar1Text := text;
-  if Assigned(FMainForm) then
-    FMainForm.sbStatusBar.Panels[1].Text := text;
+  TUserInterface.FLoading.Invoke(Sender, message, curPos, totalCount);
 end;
 
-class procedure TUserInterface.SetStatusBar2Text(const text : AnsiString); static;
+class procedure TUserInterface.NotifyStateChanged(Sender: TObject);
 begin
-  FStatusBar2Text := text;
-  if Assigned(FMainForm) then
-    FMainForm.sbStatusBar.Panels[2].Text := text;
+  TUserInterface.FStateChanged.Invoke(Sender);
 end;
 
-class procedure TUserInterface.SetMessagesNotificationText(const text : AnsiString); static;
+class procedure TUserInterface.NotifySettingsChangedEvent(Sender: TObject);
 begin
-  FMessagesNotificationText := text;
-  if Assigned(FMainForm.SyncControl) then begin
-    if (text = '') then
-      FMainForm.SyncControl.lblReceivedMessages.Visible := false;
-    FMainForm.SyncControl.lblReceivedMessages.Caption := text;
-  end;
+  TUserInterface.FSettingsChanged.Invoke(Sender);
 end;
 
-{%endregion}
-
-{%region Handlers -- TODO: many need to be refactored out with TNotifyManyEvent}
-
-class procedure TUserInterface.OnSettingsChanged(Sender: TObject);
-Var wa : Boolean;
-  i : Integer;
+class procedure TUserInterface.NotifyAccountsChangedEvent(Sender: TObject);
 begin
-  if TSettings.SaveLogFiles then begin
-    if TSettings.SaveDebugLogs then
-      FLog.SaveTypes := CT_TLogTypes_ALL
-    else
-      FLog.SaveTypes := CT_TLogTypes_DEFAULT;
-    FLog.FileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'PascalCointWallet.log';
-  end else begin
-    FLog.SaveTypes := [];
-    FLog.FileName := '';
-  end;
-  if Assigned(FNode) then begin
-    wa := FNode.NetServer.Active;
-    FNode.NetServer.Port := TSettings.InternetServerPort;
-    FNode.NetServer.Active := wa;
-    FNode.Operations.BlockPayload := TSettings.MinerName;
-    FNode.NodeLogFilename := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'blocks.log';
-  end;
-  if Assigned(FPoolMiningServer) then begin
-    if FPoolMiningServer.Port <> TSettings.MinerServerRpcPort then begin
-      FPoolMiningServer.Active := false;
-      FPoolMiningServer.Port := TSettings.MinerServerRpcPort;
-    end;
-    FPoolMiningServer.Active :=TSettings.MinerServerRpcActive;
-    FPoolMiningServer.UpdateAccountAndPayload(TWallet.MiningKey, TSettings.MinerName);
-  end;
-  if Assigned(FRPCServer) then begin
-    FRPCServer.Active := TSettings.RpcPortEnabled;
-    FRPCServer.ValidIPs := TSettings.RpcAllowedIPs;
-  end;
+  TUserInterface.FAccountsChanged.Invoke(Sender);
 end;
 
-class procedure TUserInterface.OnAccountsChanged(Sender: TObject);
+class procedure TUserInterface.NotifyBlocksChangedEvent(Sender: TObject);
 begin
-  FUILock.Acquire;
-  Try
-    if Assigned(FAccountExplorer) then
-      FAccountExplorer.RefreshAccountsGrid(true);
-  finally
-    FUILock.Release;
-  end;
+  TUserInterface.FBlocksChanged.Invoke(Sender);
 end;
 
-class procedure TUserInterface.OnBlocksChanged(Sender: TObject);
+class procedure TUserInterface.NotifyNodeMessageEventEvent(NetConnection: TNetConnection; MessageData: TRawBytes);
 begin
-  FUILock.Acquire;
-  try
-    try
-      if Assigned(FAccountExplorer) then
-        FAccountExplorer.RefreshAccountsGrid(false);
-      FMainForm.SyncControl.UpdateBlockChainState;
-    except
-      On E:Exception do begin
-        E.Message := 'Error at OnNewAccount '+E.Message;
-        Raise;
-      end;
-    end;
-  finally
-    FUILock.Release;
-  end;
+  TUserInterface.FNodeMessageEvent.Invoke(NetConnection, MessageData);
 end;
 
-class procedure TUserInterface.OnNodeMessageEvent(NetConnection: TNetConnection; MessageData: TRawBytes);
+class procedure TUserInterface.NotifyReceivedHelloMessageEvent(Sender: TObject);
 begin
-  FUILock.Acquire;
-  Try
-    if Assigned(FMessagesForm) then
-      FMessagesForm.OnNodeMessageEvent(NetConnection, MessageData);
-  finally
-      FUILock.Release;
-  end;
+  TUserInterface.FReceivedHelloMessage.Invoke(Sender);;
 end;
 
-class procedure TUserInterface.OnReceivedHelloMessage(Sender: TObject);
-Var nsarr : TNodeServerAddressArray;
-  i : Integer;
-  s : AnsiString;
+class procedure TUserInterface.NotifyNetStatisticsChangedEvent(Sender: TObject);
 begin
-  // No lock required
-  //CheckMining;
-  // Update node servers Peer Cache
-  nsarr := TNetData.NetData.NodeServersAddresses.GetValidNodeServers(true,0);
-  s := '';
-  for i := low(nsarr) to High(nsarr) do begin
-    if (s<>'') then s := s+';';
-    s := s + nsarr[i].ip+':'+IntToStr( nsarr[i].port );
-  end;
-  TSettings.PeerCache := s;
-  TNode.Node.PeerCache := s;
+  TUserInterface.FNetStatisticsChanged.Invoke(Sender);
 end;
 
-class procedure TUserInterface.OnNetStatisticsChanged(Sender: TObject);
-Var NS : TNetStatistics;
+class procedure TUserInterface.NotifyNetConnectionsUpdatedEvent(Sender: TObject);
 begin
-  FUILock.Acquire;   // TODO - lock may not be required
-  Try
-    //HS CheckMining;
-    if Assigned(FNode) then begin
-      If FNode.NetServer.Active then begin
-        StatusBar0Text := 'Active (Port '+Inttostr(FNode.NetServer.Port)+')';
-      end else StatusBar0Text := 'Server stopped';
-      NS := TNetData.NetData.NetStatistics;
-      StatusBar1Text := Format('Connections:%d Clients:%d Servers:%d - Rcvd:%d Kb Send:%d Kb',
-        [NS.ActiveConnections,NS.ClientsConnections,NS.ServersConnections,NS.BytesReceived DIV 1024,NS.BytesSend DIV 1024]);
-    end else begin
-      StatusBar0Text := '';
-      StatusBar1Text := '';
-    end;
-  finally
-      FUILock.Release;
-  end;
+  TUserInterface.FNetConnectionsUpdated.Invoke(Sender);
 end;
 
-class procedure TUserInterface.OnNetConnectionsUpdated(Sender: TObject);
+class procedure TUserInterface.NotifyNetNodeServersUpdatedEvent(Sender: TObject);
 begin
-  try
-    FUILock.Acquire;
-    if Assigned(FNodesForm) then
-      FNodesForm.OnNetConnectionsUpdated;
-  finally
-    FUILock.Release;
-  end;
+  TUserInterface.NetNodeServersUpdated.Invoke(Sender);
 end;
 
-class procedure TUserInterface.OnNetNodeServersUpdated(Sender: TObject);
+class procedure TUserInterface.NotifyNetBlackListUpdatedEvent(Sender: TObject);
 begin
-  try
-    FUILock.Acquire;
-    if Assigned(FNodesForm) then
-      FNodesForm.OnNetNodeServersUpdated;
-  finally
-    FUILock.Release;
-  end;
+  TUserInterface.FNetBlackListUpdated.Invoke(Sender);
 end;
 
-class procedure TUserInterface.OnNetBlackListUpdated(Sender: TObject);
+class procedure TUserInterface.NotifyMiningServerNewBlockFoundEvent(Sender: TObject);
 begin
-  try
-    FUILock.Acquire;
-    if Assigned(FNodesForm) then
-      FNodesForm.OnNetBlackListUpdated;
-  finally
-    FUILock.Release;
-  end;
+  TUserInterface.FMiningServerNewBlockFound.Invoke(Sender);
 end;
 
-class procedure TUserInterface.OnMiningServerNewBlockFound(Sender: TObject);
+class procedure TUserInterface.NotifyUIRefreshTimerEvent(Sender: TObject);
 begin
-  // No lock required
-  FPoolMiningServer.MinerAccountKey := TWallet.MiningKey;
+  TUserInterface.FUIRefreshTimer.Invoke(Sender);
 end;
 
-class procedure TUserInterface.OnTimerUpdateStatusTimer(Sender: TObject);
-begin
-  Try
-    RefreshConnectionStatusDisplay;
-    FMainForm.SyncControl.UpdateBlockChainState;
-    FMainForm.SyncControl.UpdateNodeStatus;
-  Except
-    On E:Exception do begin
-      E.Message := 'Exception at TimerUpdate '+E.ClassName+': '+E.Message;
-      TLog.NewLog(lterror,ClassName,E.Message);
-    end;
-  End;
-end;
+{%endregion}
 
-class procedure TUserInterface.OnTrayIconDblClick(Sender: TObject);
+{ TLoadDatabaseThread }
+
+procedure TLoadDatabaseThread.OnProgressNotify(sender: TObject; const message: AnsiString; curPos, totalCount: Int64);
 begin
-  RunInForeground;
+  TUserInterface.NotifyLoadingEvent(sender, message, curPos, totalCount);
 end;
 
-class procedure TUserInterface.OnSubFormDestroyed(Sender: TObject);
+procedure TLoadDatabaseThread.OnLoaded;
 begin
-  try
-    FUILock.Acquire;
-    if Sender = FAccountExplorer then
-      FAccountExplorer := nil // form free's on close
-    else if Sender = FPendingOperationForm then
-      FPendingOperationForm := nil // form free's on close
-    else if Sender = FOperationsExplorerForm then
-      FOperationsExplorerForm := nil // form free's on close
-    else if Sender = FBlockExplorerForm then
-      FBlockExplorerForm := nil // form free's on close
-    else if Sender = FLogsForm then
-      FLogsForm := nil // form free's on close
-    else if Sender = FNodesForm then
-      FNodesForm := nil // form free's on close
-    else if Sender = FMessagesForm then
-      FMessagesForm := nil
-    else
-      raise Exception.Create('Internal Error: [NotifySubFormDestroyed] encountered an unknown sub-form instance');
-  finally
-    FUILock.Release;
-  end;
+  TUserInterface.NotifyLoadedEvent(Self);
 end;
 
-{%endregion}
-
-{ TLoadDatabaseThread }
-
 procedure TLoadDatabaseThread.BCExecute;
 begin
   // Read Operations saved from disk
-  TNode.Node.InitSafeboxAndOperations; // New Build 2.1.4 to load pending operations buffer
+  TNode.Node.InitSafeboxAndOperations($FFFFFFFF, OnProgressNotify);
   TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
   TNode.Node.NetServer.Active := true;
-  Synchronize( TUserInterface.FinishedLoadingDatabase );
+  Synchronize( OnLoaded );
 end;
 
 initialization
@@ -1111,4 +1101,3 @@ initialization
 finalization
 // TODO - final cleanup here, show a modal dialog?
 end.
-

+ 1 - 1
src/pascalcoin_miner.lpi

@@ -50,7 +50,7 @@
     </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="core;libraries\pasopencl;libraries\synapse;libraries\pascalcoin"/>
+      <OtherUnitFiles Value="core;libraries\pasopencl;libraries\synapse;libraries\sphere10;libraries\hashlib4pascal;libraries\generics.collections;libraries\pascalcoin"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <CodeGeneration>

+ 4 - 0
src/pascalcoin_wallet.lpi

@@ -243,12 +243,16 @@
       <Unit41>
         <Filename Value="gui\UFRMAccountInfo.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMAccountInfo"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit41>
       <Unit42>
         <Filename Value="gui\UFRMMemoText.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMMemoText"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit42>
       <Unit43>
         <Filename Value="gui\UFRMSaleAccounts.pas"/>