瀏覽代碼

Showing overall synchronization progress on progress window and taskbar (2nd phase - with overall status - based on sizes)

Source commit: 119383f9b2c39a6924260e5ce8be13ebb46e6850
Martin Prikryl 7 年之前
父節點
當前提交
ad4fb2091d

+ 96 - 45
source/core/FileOperationProgress.cpp

@@ -8,6 +8,34 @@
 //---------------------------------------------------------------------------
 #define TRANSFER_BUF_SIZE 32768
 //---------------------------------------------------------------------------
+TFileOperationProgressType::TPersistence::TPersistence()
+{
+  Clear(true, true);
+}
+//---------------------------------------------------------------------------
+bool TFileOperationProgressType::IsIndeterminateOperation(TFileOperation Operation)
+{
+  return (Operation == foCalculateSize);
+}
+//---------------------------------------------------------------------------
+void TFileOperationProgressType::TPersistence::Clear(bool Batch, bool Speed)
+{
+  if (Batch)
+  {
+    TotalTransferred = 0;
+    StartTime = Now();
+    SkipToAll = false;
+    BatchOverwrite = boNo;
+    CPSLimit = 0;
+    CounterSet = false;
+  }
+  if (Speed)
+  {
+    Ticks.clear();
+    TotalTransferredThen.clear();
+  }
+}
+//---------------------------------------------------------------------------
 __fastcall TFileOperationProgressType::TFileOperationProgressType()
 {
   FOnProgress = NULL;
@@ -41,6 +69,8 @@ void __fastcall TFileOperationProgressType::Init()
 {
   FSection = new TCriticalSection();
   FUserSelectionsSection = new TCriticalSection();
+  FRestored = false;
+  FPersistence.Side = osCurrent; // = undefined value
 }
 //---------------------------------------------------------------------------
 void __fastcall TFileOperationProgressType::Assign(const TFileOperationProgressType & Other)
@@ -62,7 +92,7 @@ void __fastcall TFileOperationProgressType::AssignButKeepSuspendState(const TFil
   Assign(Other);
 }
 //---------------------------------------------------------------------------
-void __fastcall TFileOperationProgressType::Clear()
+void __fastcall TFileOperationProgressType::DoClear(bool Batch, bool Speed)
 {
   FFileName = L"";
   FFullFileName = L"";
@@ -71,30 +101,28 @@ void __fastcall TFileOperationProgressType::Clear()
   FCount = -1;
   FFilesFinished = 0;
   FFilesFinishedSuccessfully = 0;
-  FStartTime = Now();
   FSuspended = false;
   FSuspendTime = 0;
   FInProgress = false;
   FDone = false;
   FFileInProgress = false;
-  FTotalTransferred = 0;
   FTotalSkipped = 0;
   FTotalSize = 0;
   FSkippedSize = 0;
   FTotalSizeSet = false;
   FOperation = foNone;
   FTemp = false;
-  FSkipToAll = false;
-  FBatchOverwrite = boNo;
+  FPersistence.Clear(Batch, Speed);
   // to bypass check in ClearTransfer()
   FTransferSize = 0;
-  FCPSLimit = 0;
-  FTicks.clear();
-  FTotalTransferredThen.clear();
-  FCounterSet = false;
   ClearTransfer();
 }
 //---------------------------------------------------------------------------
+void __fastcall TFileOperationProgressType::Clear()
+{
+  DoClear(true, true);
+}
+//---------------------------------------------------------------------------
 void __fastcall TFileOperationProgressType::ClearTransfer()
 {
   if ((TransferSize > 0) && (TransferredSize < TransferSize))
@@ -125,15 +153,16 @@ void __fastcall TFileOperationProgressType::Start(TFileOperation AOperation,
 
   {
     TGuard Guard(FSection); // not really needed, just for consistency
-    Clear();
+    DoClear(!FRestored, (FPersistence.Side != osCurrent) && (FPersistence.Side != ASide));
+    FTotalTransferBase = FPersistence.TotalTransferred;
     FOperation = AOperation;
-    FSide = ASide;
+    FPersistence.Side = ASide;
     FCount = ACount;
     FInProgress = true;
     FCancel = csContinue;
     FDirectory = ADirectory;
     FTemp = ATemp;
-    FCPSLimit = ACPSLimit;
+    FPersistence.CPSLimit = ACPSLimit;
   }
 
   try
@@ -194,9 +223,9 @@ void __fastcall TFileOperationProgressType::Resume()
     // by the time the progress was suspended
     unsigned long Stopped = (GetTickCount() - FSuspendTime);
     size_t i = 0;
-    while (i < FTicks.size())
+    while (i < FPersistence.Ticks.size())
     {
-      FTicks[i] += Stopped;
+      FPersistence.Ticks[i] += Stopped;
       ++i;
     }
   }
@@ -236,7 +265,15 @@ int __fastcall TFileOperationProgressType::TotalTransferProgress() const
 {
   TGuard Guard(FSection);
   DebugAssert(TotalSizeSet);
-  int Result = FTotalSize > 0 ? (int)(((FTotalTransferred + FTotalSkipped) * 100)/FTotalSize) : 0;
+  int Result;
+  if (FTotalSize > 0)
+  {
+    Result = (int)(((FPersistence.TotalTransferred - FTotalTransferBase + FTotalSkipped) * 100)/FTotalSize);
+  }
+  else
+  {
+    Result = 0;
+  }
   return Result < 100 ? Result : 100;
 }
 //---------------------------------------------------------------------------
@@ -328,9 +365,9 @@ bool __fastcall TFileOperationProgressType::IsLocallyDone()
 //---------------------------------------------------------------------------
 void __fastcall TFileOperationProgressType::SetSpeedCounters()
 {
-  if ((CPSLimit > 0) && !FCounterSet)
+  if ((CPSLimit > 0) && !FPersistence.CounterSet)
   {
-    FCounterSet = true;
+    FPersistence.CounterSet = true;
     Configuration->Usage->Inc(L"SpeedLimitUses");
   }
 }
@@ -510,7 +547,7 @@ unsigned long __fastcall TFileOperationProgressType::GetCPSLimit()
   else
   {
     TGuard Guard(FSection);
-    Result = FCPSLimit;
+    Result = FPersistence.CPSLimit;
   }
   return Result;
 }
@@ -524,7 +561,7 @@ void __fastcall TFileOperationProgressType::SetCPSLimit(unsigned long ACPSLimit)
   else
   {
     TGuard Guard(FSection);
-    FCPSLimit = ACPSLimit;
+    FPersistence.CPSLimit = ACPSLimit;
   }
 }
 //---------------------------------------------------------------------------
@@ -538,7 +575,7 @@ TBatchOverwrite __fastcall TFileOperationProgressType::GetBatchOverwrite()
   else
   {
     TGuard Guard(FSection); // not really needed
-    Result = FBatchOverwrite;
+    Result = FPersistence.BatchOverwrite;
   }
   return Result;
 }
@@ -552,7 +589,7 @@ void __fastcall TFileOperationProgressType::SetBatchOverwrite(TBatchOverwrite AB
   else
   {
     TGuard Guard(FSection); // not really needed
-    FBatchOverwrite = ABatchOverwrite;;
+    FPersistence.BatchOverwrite = ABatchOverwrite;;
   }
 }
 //---------------------------------------------------------------------------
@@ -566,7 +603,7 @@ bool __fastcall TFileOperationProgressType::GetSkipToAll()
   else
   {
     TGuard Guard(FSection); // not really needed
-    Result = FSkipToAll;
+    Result = FPersistence.SkipToAll;
   }
   return Result;
 }
@@ -580,7 +617,7 @@ void __fastcall TFileOperationProgressType::SetSkipToAll()
   else
   {
     TGuard Guard(FSection); // not really needed
-    FSkipToAll = true;
+    FPersistence.SkipToAll = true;
   }
 }
 //---------------------------------------------------------------------------
@@ -600,11 +637,11 @@ void __fastcall TFileOperationProgressType::RollbackTransferFromTotals(__int64 A
 {
   TGuard Guard(FSection);
 
-  DebugAssert(ATransferredSize <= FTotalTransferred);
+  DebugAssert(ATransferredSize <= FPersistence.TotalTransferred - FTotalTransferBase);
   DebugAssert(ASkippedSize <= FTotalSkipped);
-  FTotalTransferred -= ATransferredSize;
-  FTicks.clear();
-  FTotalTransferredThen.clear();
+  FPersistence.TotalTransferred -= ATransferredSize;
+  FPersistence.Ticks.clear();
+  FPersistence.TotalTransferredThen.clear();
   FTotalSkipped -= ASkippedSize;
 
   if (FParent != NULL)
@@ -627,27 +664,27 @@ void __fastcall TFileOperationProgressType::AddTransferredToTotals(__int64 ASize
 {
   TGuard Guard(FSection);
 
-  FTotalTransferred += ASize;
+  FPersistence.TotalTransferred += ASize;
   if (ASize >= 0)
   {
     unsigned long Ticks = GetTickCount();
-    if (FTicks.empty() ||
-        (FTicks.back() > Ticks) || // ticks wrap after 49.7 days
-        ((Ticks - FTicks.back()) >= MSecsPerSec))
+    if (FPersistence.Ticks.empty() ||
+        (FPersistence.Ticks.back() > Ticks) || // ticks wrap after 49.7 days
+        ((Ticks - FPersistence.Ticks.back()) >= MSecsPerSec))
     {
-      FTicks.push_back(Ticks);
-      FTotalTransferredThen.push_back(FTotalTransferred);
+      FPersistence.Ticks.push_back(Ticks);
+      FPersistence.TotalTransferredThen.push_back(FPersistence.TotalTransferred);
     }
 
-    if (FTicks.size() > 10)
+    if (FPersistence.Ticks.size() > 10)
     {
-      FTicks.erase(FTicks.begin());
-      FTotalTransferredThen.erase(FTotalTransferredThen.begin());
+      FPersistence.Ticks.erase(FPersistence.Ticks.begin());
+      FPersistence.TotalTransferredThen.erase(FPersistence.TotalTransferredThen.begin());
     }
   }
   else
   {
-    FTicks.clear();
+    FPersistence.Ticks.clear();
   }
 
   if (FParent != NULL)
@@ -762,7 +799,7 @@ unsigned int __fastcall TFileOperationProgressType::CPS()
 unsigned int __fastcall TFileOperationProgressType::GetCPS()
 {
   unsigned int Result;
-  if (FTicks.empty())
+  if (FPersistence.Ticks.empty())
   {
     Result = 0;
   }
@@ -770,14 +807,14 @@ unsigned int __fastcall TFileOperationProgressType::GetCPS()
   {
     unsigned long Ticks = (Suspended ? FSuspendTime : GetTickCount());
     unsigned long TimeSpan;
-    if (Ticks < FTicks.front())
+    if (Ticks < FPersistence.Ticks.front())
     {
       // clocks has wrapped, guess 10 seconds difference
       TimeSpan = 10000;
     }
     else
     {
-      TimeSpan = (Ticks - FTicks.front());
+      TimeSpan = (Ticks - FPersistence.Ticks.front());
     }
 
     if (TimeSpan == 0)
@@ -786,7 +823,7 @@ unsigned int __fastcall TFileOperationProgressType::GetCPS()
     }
     else
     {
-      __int64 Transferred = (FTotalTransferred - FTotalTransferredThen.front());
+      __int64 Transferred = (FPersistence.TotalTransferred - FPersistence.TotalTransferredThen.front());
       Result = (unsigned int)(Transferred * MSecsPerSec / TimeSpan);
     }
   }
@@ -812,10 +849,10 @@ TDateTime __fastcall TFileOperationProgressType::TotalTimeLeft()
   DebugAssert(FTotalSizeSet);
   unsigned int CurCps = GetCPS();
   // sanity check
-  if ((CurCps > 0) && (FTotalSize > FTotalSkipped + FTotalTransferred))
+  __int64 Processed = FTotalSkipped + FPersistence.TotalTransferred - FTotalTransferBase;
+  if ((CurCps > 0) && (FTotalSize > Processed))
   {
-    return TDateTime((double)((double)(FTotalSize - FTotalSkipped - FTotalTransferred) / CurCps) /
-      SecsPerDay);
+    return TDateTime((double)((double)(FTotalSize - Processed) / CurCps) / SecsPerDay);
   }
   else
   {
@@ -826,7 +863,7 @@ TDateTime __fastcall TFileOperationProgressType::TotalTimeLeft()
 __int64 __fastcall TFileOperationProgressType::GetTotalTransferred()
 {
   TGuard Guard(FSection);
-  return FTotalTransferred;
+  return FPersistence.TotalTransferred;
 }
 //---------------------------------------------------------------------------
 __int64 __fastcall TFileOperationProgressType::GetTotalSize()
@@ -879,3 +916,17 @@ UnicodeString __fastcall TFileOperationProgressType::GetLogStr(bool Done)
   UnicodeString CPSStr = FormatSize(CPS());
   return FORMAT(L"Transferred: %s, %s: %s, CPS: %s/s", (Transferred, TimeLabel, TimeStr, CPSStr));
 }
+//---------------------------------------------------------------------------
+void __fastcall TFileOperationProgressType::Store(TPersistence & Persistence)
+{
+  // in this current use, we do not need this to be guarded actually, as it's used only for a single-threaded synchronization
+  TGuard Guard(FSection);
+  Persistence = FPersistence;
+}
+//---------------------------------------------------------------------------
+void __fastcall TFileOperationProgressType::Restore(TPersistence & Persistence)
+{
+  TGuard Guard(FSection);
+  FPersistence = Persistence;
+  FRestored = true;
+}

+ 33 - 11
source/core/FileOperationProgress.h

@@ -22,9 +22,29 @@ typedef void __fastcall (__closure *TFileOperationFinished)
 //---------------------------------------------------------------------------
 class TFileOperationProgressType
 {
+public:
+  class TPersistence
+  {
+  friend class TFileOperationProgressType;
+  public:
+    TPersistence();
+
+  private:
+    void Clear(bool Batch, bool Speed);
+
+    TDateTime StartTime;
+    TBatchOverwrite BatchOverwrite;
+    bool SkipToAll;
+    unsigned long CPSLimit;
+    bool CounterSet;
+    std::vector<unsigned long> Ticks;
+    std::vector<__int64> TotalTransferredThen;
+    TOperationSide Side;
+    __int64 TotalTransferred;
+  };
+
 private:
   TFileOperation FOperation;
-  TOperationSide FSide;
   UnicodeString FFileName;
   UnicodeString FFullFileName;
   UnicodeString FDirectory;
@@ -41,15 +61,12 @@ private:
   bool FFileInProgress;
   TCancelStatus FCancel;
   int FCount;
-  TDateTime FStartTime;
-  __int64 FTotalTransferred;
+  __int64 FTotalTransferBase;
   __int64 FTotalSkipped;
   __int64 FTotalSize;
-  TBatchOverwrite FBatchOverwrite;
-  bool FSkipToAll;
-  unsigned long FCPSLimit;
   bool FTotalSizeSet;
   bool FSuspended;
+  bool FRestored;
   TFileOperationProgressType * FParent;
 
   // when it was last time suspended (to calculate suspend time in Resume())
@@ -63,9 +80,7 @@ private:
   bool FReset;
   unsigned int FLastSecond;
   unsigned long FRemainingCPS;
-  bool FCounterSet;
-  std::vector<unsigned long> FTicks;
-  std::vector<__int64> FTotalTransferredThen;
+  TPersistence FPersistence;
   TCriticalSection * FSection;
   TCriticalSection * FUserSelectionsSection;
 
@@ -74,6 +89,8 @@ private:
   unsigned long __fastcall GetCPSLimit();
   TBatchOverwrite __fastcall GetBatchOverwrite();
   bool __fastcall GetSkipToAll();
+  TDateTime __fastcall GetStartTime() const { return FPersistence.StartTime; };
+  TOperationSide __fastcall GetSide() const { return FPersistence.Side; };
 
 protected:
   void __fastcall ClearTransfer();
@@ -86,12 +103,13 @@ protected:
   unsigned int __fastcall GetCPS();
   void __fastcall Init();
   static bool __fastcall PassCancelToParent(TCancelStatus ACancel);
+  void __fastcall DoClear(bool Batch, bool Speed);
 
 public:
   // common data
   __property TFileOperation Operation = { read = FOperation };
   // on what side if operation being processed (local/remote), source of copy
-  __property TOperationSide Side = { read = FSide };
+  __property TOperationSide Side = { read = GetSide };
   __property int Count =  { read = FCount };
   __property UnicodeString FileName =  { read = FFileName };
   __property UnicodeString FullFileName = { read = FFullFileName };
@@ -112,7 +130,7 @@ public:
   __property bool FileInProgress = { read = FFileInProgress };
   __property TCancelStatus Cancel = { read = GetCancel };
   // when operation started
-  __property TDateTime StartTime = { read = FStartTime };
+  __property TDateTime StartTime = { read = GetStartTime };
   // bytes transferred
   __property __int64 TotalTransferred = { read = GetTotalTransferred };
   __property __int64 TotalSize = { read = GetTotalSize };
@@ -186,6 +204,10 @@ public:
   void __fastcall SetBatchOverwrite(TBatchOverwrite ABatchOverwrite);
   void __fastcall SetSkipToAll();
   UnicodeString __fastcall GetLogStr(bool Done);
+  void __fastcall Store(TPersistence & Persistence);
+  void __fastcall Restore(TPersistence & Persistence);
+
+  static bool IsIndeterminateOperation(TFileOperation Operation);
 };
 //---------------------------------------------------------------------------
 class TSuspendFileOperationProgress

+ 2 - 5
source/core/FtpFileSystem.cpp

@@ -1252,9 +1252,7 @@ void __fastcall TFTPFileSystem::CalculateFilesChecksum(const UnicodeString & Alg
   TCalculatedChecksumEvent OnCalculatedChecksum)
 {
   TFileOperationProgressType Progress(&FTerminal->DoProgress, &FTerminal->DoFinished);
-  Progress.Start(foCalculateChecksum, osRemote, FileList->Count);
-
-  FTerminal->FOperationProgress = &Progress;
+  FTerminal->OperationStart(Progress, foCalculateChecksum, osRemote, FileList->Count);
 
   try
   {
@@ -1282,8 +1280,7 @@ void __fastcall TFTPFileSystem::CalculateFilesChecksum(const UnicodeString & Alg
   }
   __finally
   {
-    FTerminal->FOperationProgress = NULL;
-    Progress.Stop();
+    FTerminal->OperationStop(Progress);
   }
 }
 //---------------------------------------------------------------------------

+ 94 - 9
source/core/RemoteFiles.cpp

@@ -2903,23 +2903,108 @@ bool __fastcall TSynchronizeChecklist::IsItemSizeIrrelevant(TAction Action)
 //---------------------------------------------------------------------------
 TSynchronizeProgress::TSynchronizeProgress(const TSynchronizeChecklist * Checklist)
 {
-  for (int Index = 0; Index < Checklist->Count; Index++)
+  FTotalSize = -1;
+  FProcessedSize = 0;
+  FChecklist = Checklist;
+}
+//---------------------------------------------------------------------------
+__int64 TSynchronizeProgress::ItemSize(const TSynchronizeChecklist::TItem * ChecklistItem) const
+{
+  __int64 Result;
+  switch (ChecklistItem->Action)
+  {
+    case TSynchronizeChecklist::saDeleteRemote:
+    case TSynchronizeChecklist::saDeleteLocal:
+      Result = ChecklistItem->IsDirectory ? 1024*1024 : 100*1024;
+      break;
+
+    default:
+      if (ChecklistItem->HasSize())
+      {
+        Result = ChecklistItem->GetSize();
+      }
+      else
+      {
+        DebugAssert(ChecklistItem->IsDirectory);
+        Result = 1024*1024;
+      }
+      break;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void TSynchronizeProgress::ItemProcessed(const TSynchronizeChecklist::TItem * ChecklistItem)
+{
+  DebugAssert(FChecklist->Item[FCurrentItem] == ChecklistItem);
+  FProcessedSize += ItemSize(ChecklistItem);
+
+  do
+  {
+    FCurrentItem++;
+  }
+  while ((FCurrentItem < FChecklist->Count) && !FChecklist->Item[FCurrentItem]->Checked);
+
+  if (FCurrentItem >= FChecklist->Count)
   {
-    const TSynchronizeChecklist::TItem * ChecklistItem = Checklist->Item[Index];
-    if (ChecklistItem->Checked)
+    FCurrentItem = -1;
+  }
+}
+//---------------------------------------------------------------------------
+__int64 TSynchronizeProgress::GetProcessed(const TFileOperationProgressType * CurrentItemOperationProgress) const
+{
+  DebugAssert(!TFileOperationProgressType::IsIndeterminateOperation(CurrentItemOperationProgress->Operation));
+
+  // Need to calculate the total size on the first call only,
+  // as at the time the contrusctor it called, we usually do not have sizes of folders caculated yet.
+  if (FTotalSize < 0)
+  {
+    FTotalSize = 0;
+    FCurrentItem = -1;
+
+    for (int Index = 0; Index < FChecklist->Count; Index++)
     {
-      FItems.insert(std::make_pair(ChecklistItem, TItemData()));
+      const TSynchronizeChecklist::TItem * ChecklistItem = FChecklist->Item[Index];
+      if (ChecklistItem->Checked)
+      {
+        FTotalSize += ItemSize(ChecklistItem);
+        if (FCurrentItem < 0)
+        {
+          FCurrentItem = Index;
+        }
+      }
     }
   }
-  FItemsProcessed = 0;
+
+  DebugAssert(FCurrentItem >= 0);
+  __int64 CurrentItemSize = ItemSize(FChecklist->Item[FCurrentItem]);
+  // For (single-item-)delete operation, this should return 0
+  int CurrentItemProgress = CurrentItemOperationProgress->OverallProgress();
+  __int64 CurrentItemProcessedSize = (CurrentItemSize * CurrentItemProgress) / 100;
+  return (FProcessedSize + CurrentItemProcessedSize);
 }
 //---------------------------------------------------------------------------
-void TSynchronizeProgress::ItemProcessed(const TSynchronizeChecklist::TItem * /*ChecklistItem*/)
+int TSynchronizeProgress::Progress(const TFileOperationProgressType * CurrentItemOperationProgress) const
 {
-  FItemsProcessed++;
+  __int64 Processed = GetProcessed(CurrentItemOperationProgress);
+  int Result;
+  if (FTotalSize > 0)
+  {
+    Result = (Processed * 100) / FTotalSize;
+  }
+  else
+  {
+    Result = 0;
+  }
+  return Result;
 }
 //---------------------------------------------------------------------------
-int TSynchronizeProgress::Progress() const
+TDateTime TSynchronizeProgress::TimeLeft(const TFileOperationProgressType * CurrentItemOperationProgress) const
 {
-  return (FItemsProcessed * 100) / FItems.size();
+  TDateTime Result;
+  __int64 Processed = GetProcessed(CurrentItemOperationProgress);
+  if (Processed > 0)
+  {
+    Result = TDateTime(double(Now() - CurrentItemOperationProgress->StartTime) / Processed * (FTotalSize - Processed));
+  }
+  return Result;
 }

+ 10 - 8
source/core/RemoteFiles.h

@@ -517,23 +517,25 @@ private:
   static int __fastcall Compare(void * Item1, void * Item2);
 };
 //---------------------------------------------------------------------------
+class TFileOperationProgressType;
+//---------------------------------------------------------------------------
 class TSynchronizeProgress
 {
 public:
   TSynchronizeProgress(const TSynchronizeChecklist * Checklist);
 
   void ItemProcessed(const TSynchronizeChecklist::TItem * ChecklistItem);
-  int Progress() const;
+  int Progress(const TFileOperationProgressType * CurrentItemOperationProgress) const;
+  TDateTime TimeLeft(const TFileOperationProgressType * CurrentItemOperationProgress) const;
 
 private:
-  TSynchronizeChecklist * FChecklist;
-  int FItemsProcessed;
-
-  struct TItemData
-  {
-  };
+  const TSynchronizeChecklist * FChecklist;
+  mutable __int64 FTotalSize;
+  mutable int FCurrentItem;
+  __int64 FProcessedSize;
 
-  std::map<const TSynchronizeChecklist::TItem *, TItemData> FItems;
+  __int64 ItemSize(const TSynchronizeChecklist::TItem * ChecklistItem) const;
+  __int64 GetProcessed(const TFileOperationProgressType * CurrentItemOperationProgress) const;
 };
 //---------------------------------------------------------------------------
 bool __fastcall IsUnixStyleWindowsPath(const UnicodeString & Path);

+ 4 - 10
source/core/SftpFileSystem.cpp

@@ -3947,9 +3947,7 @@ bool __fastcall TSFTPFileSystem::LoadFilesProperties(TStrings * FileList)
   if (FSupport->Loaded || (FSecureShell->SshImplementation == sshiBitvise))
   {
     TFileOperationProgressType Progress(&FTerminal->DoProgress, &FTerminal->DoFinished);
-    Progress.Start(foGetProperties, osRemote, FileList->Count);
-
-    FTerminal->FOperationProgress = &Progress;
+    FTerminal->OperationStart(Progress, foGetProperties, osRemote, FileList->Count);
 
     static int LoadFilesPropertiesQueueLen = 5;
     TSFTPLoadFilesPropertiesQueue Queue(this);
@@ -3985,8 +3983,7 @@ bool __fastcall TSFTPFileSystem::LoadFilesProperties(TStrings * FileList)
     __finally
     {
       Queue.DisposeSafe();
-      FTerminal->FOperationProgress = NULL;
-      Progress.Stop();
+      FTerminal->OperationStop(Progress);
     }
     // queue is discarded here
   }
@@ -4134,7 +4131,6 @@ void __fastcall TSFTPFileSystem::CalculateFilesChecksum(const UnicodeString & Al
   TCalculatedChecksumEvent OnCalculatedChecksum)
 {
   TFileOperationProgressType Progress(&FTerminal->DoProgress, &FTerminal->DoFinished);
-  Progress.Start(foCalculateChecksum, osRemote, FileList->Count);
 
   UnicodeString NormalizedAlg = FindIdent(Alg, FChecksumAlgs.get());
   UnicodeString SftpAlg;
@@ -4149,8 +4145,7 @@ void __fastcall TSFTPFileSystem::CalculateFilesChecksum(const UnicodeString & Al
     SftpAlg = NormalizedAlg;
   }
 
-  FTerminal->FOperationProgress = &Progress;
-
+  FTerminal->OperationStart(Progress, foCalculateChecksum, osRemote, FileList->Count);
   try
   {
     DoCalculateFilesChecksum(NormalizedAlg, SftpAlg, FileList, Checksums, OnCalculatedChecksum,
@@ -4158,8 +4153,7 @@ void __fastcall TSFTPFileSystem::CalculateFilesChecksum(const UnicodeString & Al
   }
   __finally
   {
-    FTerminal->FOperationProgress = NULL;
-    Progress.Stop();
+    FTerminal->OperationStop(Progress);
   }
 }
 //---------------------------------------------------------------------------

+ 43 - 17
source/core/Terminal.cpp

@@ -932,6 +932,7 @@ __fastcall TTerminal::TTerminal(TSessionData * SessionData,
   FOnCustomCommand = NULL;
   FOnClose = NULL;
   FOnFindingFile = NULL;
+  FOperationProgressPersistence = NULL;
 
   FUseBusyCursor = True;
   FLockDirectory = L"";
@@ -3658,6 +3659,36 @@ void __fastcall TTerminal::OperationFinish(
   Progress->Finish(FileName, Success, OnceDoneOperation);
 }
 //---------------------------------------------------------------------------
+void __fastcall TTerminal::OperationStart(
+  TFileOperationProgressType & Progress, TFileOperation Operation, TOperationSide Side, int Count)
+{
+  OperationStart(Progress, Operation, Side, Count, false, UnicodeString(), 0);
+}
+//---------------------------------------------------------------------------
+void __fastcall TTerminal::OperationStart(
+  TFileOperationProgressType & Progress, TFileOperation Operation, TOperationSide Side, int Count,
+  bool Temp, const UnicodeString & Directory, unsigned long CPSLimit)
+{
+  if (FOperationProgressPersistence != NULL)
+  {
+    Progress.Restore(*FOperationProgressPersistence);
+  }
+  Progress.Start(Operation, Side, Count, Temp, Directory, CPSLimit);
+  DebugAssert(FOperationProgress == NULL);
+  FOperationProgress = &Progress;
+}
+//---------------------------------------------------------------------------
+void __fastcall TTerminal::OperationStop(TFileOperationProgressType & Progress)
+{
+  DebugAssert(FOperationProgress == &Progress);
+  if (FOperationProgressPersistence != NULL)
+  {
+    Progress.Store(*FOperationProgressPersistence);
+  }
+  FOperationProgress = NULL;
+  Progress.Stop();
+}
+//---------------------------------------------------------------------------
 bool __fastcall TTerminal::ProcessFiles(TStrings * FileList,
   TFileOperation Operation, TProcessFileEvent ProcessFile, void * Param,
   TOperationSide Side, bool Ex)
@@ -3670,9 +3701,8 @@ bool __fastcall TTerminal::ProcessFiles(TStrings * FileList,
   try
   {
     TFileOperationProgressType Progress(&DoProgress, &DoFinished);
-    Progress.Start(Operation, Side, FileList->Count);
+    OperationStart(Progress, Operation, Side, FileList->Count);
 
-    FOperationProgress = &Progress;
     try
     {
       if (Side == osRemote)
@@ -3738,8 +3768,7 @@ bool __fastcall TTerminal::ProcessFiles(TStrings * FileList,
     }
     __finally
     {
-      FOperationProgress = NULL;
-      Progress.Stop();
+      OperationStop(Progress);
     }
   }
   catch (...)
@@ -5212,7 +5241,7 @@ bool __fastcall TTerminal::CalculateLocalFilesSize(TStrings * FileList,
   bool Result = false;
   TFileOperationProgressType OperationProgress(&DoProgress, &DoFinished);
   TOnceDoneOperation OnceDoneOperation = odoIdle;
-  OperationProgress.Start(foCalculateSize, osLocal, FileList->Count);
+  OperationStart(OperationProgress, foCalculateSize, osLocal, FileList->Count);
   try
   {
     TCalculateSizeParams Params;
@@ -5222,8 +5251,6 @@ bool __fastcall TTerminal::CalculateLocalFilesSize(TStrings * FileList,
     Params.Files = NULL;
     Params.Result = true;
 
-    DebugAssert(!FOperationProgress);
-    FOperationProgress = &OperationProgress;
     UnicodeString LastDirPath;
     for (int Index = 0; Params.Result && (Index < FileList->Count); Index++)
     {
@@ -5269,8 +5296,7 @@ bool __fastcall TTerminal::CalculateLocalFilesSize(TStrings * FileList,
   }
   __finally
   {
-    FOperationProgress = NULL;
-    OperationProgress.Stop();
+    OperationStop(OperationProgress);
   }
 
   if (OnceDoneOperation != odoIdle)
@@ -5916,6 +5942,9 @@ void __fastcall TTerminal::SynchronizeApply(
   }
 
   BeginTransaction();
+  TValueRestorer<TFileOperationProgressType::TPersistence *> OperationProgressPersistenceRestorer(FOperationProgressPersistence);
+  TFileOperationProgressType::TPersistence OperationProgressPersistence;
+  FOperationProgressPersistence = &OperationProgressPersistence;
 
   try
   {
@@ -6597,10 +6626,10 @@ bool __fastcall TTerminal::CopyToRemote(
 
     FLastProgressLogged = GetTickCount();
     TFileOperationProgressType OperationProgress(&DoProgress, &DoFinished);
-    OperationProgress.Start((Params & cpDelete ? foMove : foCopy), osLocal,
+    OperationStart(
+      OperationProgress, (Params & cpDelete ? foMove : foCopy), osLocal,
       FilesToCopy->Count, Params & cpTemporary, TargetDir, CopyParam->CPSLimit);
 
-    FOperationProgress = &OperationProgress;
     bool CollectingUsage = false;
     try
     {
@@ -6661,8 +6690,7 @@ bool __fastcall TTerminal::CopyToRemote(
         Configuration->Usage->Inc(L"UploadTime", CounterTime);
         Configuration->Usage->SetMax(L"MaxUploadTime", CounterTime);
       }
-      OperationProgress.Stop();
-      FOperationProgress = NULL;
+      OperationStop(OperationProgress);
     }
   }
   catch (Exception &E)
@@ -7047,10 +7075,9 @@ bool __fastcall TTerminal::CopyToLocal(
       }
     }
 
-    OperationProgress.Start((Params & cpDelete ? foMove : foCopy), osRemote,
+    OperationStart(OperationProgress, (Params & cpDelete ? foMove : foCopy), osRemote,
       FilesToCopy->Count, Params & cpTemporary, TargetDir, CopyParam->CPSLimit);
 
-    FOperationProgress = &OperationProgress;
     bool CollectingUsage = false;
     try
     {
@@ -7116,8 +7143,7 @@ bool __fastcall TTerminal::CopyToLocal(
         Configuration->Usage->Inc(L"DownloadTime", CounterTime);
         Configuration->Usage->SetMax(L"MaxDownloadTime", CounterTime);
       }
-      FOperationProgress = NULL;
-      OperationProgress.Stop();
+      OperationStop(OperationProgress);
     }
   }
   __finally

+ 7 - 1
source/core/Terminal.h

@@ -226,6 +226,7 @@ private:
   TEncryptedFileNames FEncryptedFileNames;
   std::set<UnicodeString> FFoldersScannedForEncryptedFiles;
   RawByteString FEncryptKey;
+  TFileOperationProgressType::TPersistence * FOperationProgressPersistence;
 
   void __fastcall CommandError(Exception * E, const UnicodeString Msg);
   unsigned int __fastcall CommandError(Exception * E, const UnicodeString Msg,
@@ -382,7 +383,12 @@ protected:
   void __fastcall OperationFinish(
     TFileOperationProgressType * Progress, const void * Item, const UnicodeString & FileName,
     bool Success, TOnceDoneOperation & OnceDoneOperation);
-
+  void __fastcall OperationStart(
+    TFileOperationProgressType & Progress, TFileOperation Operation, TOperationSide Side, int Count);
+  void __fastcall OperationStart(
+    TFileOperationProgressType & Progress, TFileOperation Operation, TOperationSide Side, int Count,
+    bool Temp, const UnicodeString & Directory, unsigned long CPSLimit);
+  void __fastcall OperationStop(TFileOperationProgressType & Progress);
   virtual void __fastcall Information(const UnicodeString & Str, bool Status);
   virtual unsigned int __fastcall QueryUser(const UnicodeString Query,
     TStrings * MoreMessages, unsigned int Answers, const TQueryParams * Params,

+ 4 - 10
source/forms/CustomScpExplorer.cpp

@@ -832,7 +832,7 @@ void __fastcall TCustomScpExplorerForm::SetTaskbarListProgressValue(TFileOperati
     int OverallProgress;
     if (DebugAlwaysTrue(FProgressForm != NULL) && (FProgressForm->SynchronizeProgress != NULL))
     {
-      OverallProgress = FProgressForm->SynchronizeProgress->Progress();
+      OverallProgress = FProgressForm->SynchronizeProgress->Progress(ProgressData);
     }
     else
     {
@@ -1513,18 +1513,12 @@ void __fastcall TCustomScpExplorerForm::OperationProgress(
   FileOperationProgress(ProgressData);
 }
 //---------------------------------------------------------------------------
-UnicodeString __fastcall TCustomScpExplorerForm::GetProgressTitle(const TFileOperationProgressType & ProgressData)
+UnicodeString __fastcall TCustomScpExplorerForm::GetProgressTitle()
 {
   UnicodeString Result;
-  if (ProgressData.InProgress)
+  if (FProgressForm != NULL)
   {
-    TSynchronizeProgress * SynchronizeProgress = NULL;
-    if (FProgressForm != NULL)
-    {
-      SynchronizeProgress = FProgressForm->SynchronizeProgress;
-    }
-
-    Result = TProgressForm::ProgressStr(SynchronizeProgress, &ProgressData);
+    Result = FProgressForm->ProgressStr();
   }
   return Result;
 }

+ 1 - 1
source/forms/CustomScpExplorer.h

@@ -696,7 +696,7 @@ public:
   void __fastcall OperationFinished(TFileOperation Operation, TOperationSide Side,
     bool Temp, const UnicodeString & FileName, bool Success, TOnceDoneOperation & OnceDoneOperation);
   void __fastcall OperationProgress(TFileOperationProgressType & ProgressData);
-  UnicodeString __fastcall GetProgressTitle(const TFileOperationProgressType & ProgressData);
+  UnicodeString __fastcall GetProgressTitle();
   void __fastcall ShowExtendedException(TTerminal * Terminal, Exception * E);
   void __fastcall InactiveTerminalException(TTerminal * Terminal, Exception * E);
   void __fastcall TerminalReady();

+ 36 - 16
source/forms/Progress.cpp

@@ -29,11 +29,6 @@
 #pragma resource "*.dfm"
 #endif
 //---------------------------------------------------------------------
-bool __fastcall TProgressForm::IsIndeterminateOperation(TFileOperation Operation)
-{
-  return (Operation == foCalculateSize);
-}
-//---------------------------------------------------------------------
 UnicodeString __fastcall TProgressForm::ProgressStr(
   const TSynchronizeProgress * SynchronizeProgress, const TFileOperationProgressType * ProgressData)
 {
@@ -55,11 +50,20 @@ UnicodeString __fastcall TProgressForm::ProgressStr(
   UnicodeString Result = LoadStr(Id);
   if (SynchronizeProgress != NULL)
   {
-    Result = FORMAT(L"%d%% %s - %s", (SynchronizeProgress->Progress(), LoadStr(SYNCHRONIZE_PROGRESS_SYNCHRONIZE2), Result));
+    Result = FORMAT(L"%s - %s", (LoadStr(SYNCHRONIZE_PROGRESS_SYNCHRONIZE2), Result));
   }
-  else if (!IsIndeterminateOperation(ProgressData->Operation))
+  if (!TFileOperationProgressType::IsIndeterminateOperation(ProgressData->Operation))
   {
-    Result = FORMAT(L"%d%% %s", (ProgressData->OverallProgress(), Result));
+    int OverallProgress;
+    if (SynchronizeProgress != NULL)
+    {
+      OverallProgress = SynchronizeProgress->Progress(ProgressData);
+    }
+    else
+    {
+      OverallProgress = ProgressData->OverallProgress();
+    }
+    Result = FORMAT(L"%d%% %s", (OverallProgress, Result));
   }
   return Result;
 }
@@ -118,6 +122,11 @@ __fastcall TProgressForm::~TProgressForm()
   ReleaseAsModal(this, FShowAsModalStorage);
 }
 //---------------------------------------------------------------------
+UnicodeString __fastcall TProgressForm::ProgressStr()
+{
+  return FProgressStr;
+}
+//---------------------------------------------------------------------
 void __fastcall TProgressForm::UpdateControls()
 {
   DebugAssert((FData.Operation >= foCopy) && (FData.Operation <= foUnlock) &&
@@ -210,7 +219,7 @@ void __fastcall TProgressForm::UpdateControls()
 
     CancelItem->Caption = CancelCaption;
 
-    OperationProgress->Style = IsIndeterminateOperation(FData.Operation) ? pbstMarquee : pbstNormal;
+    OperationProgress->Style = TFileOperationProgressType::IsIndeterminateOperation(FData.Operation) ? pbstMarquee : pbstNormal;
 
     if (SynchronizeProgress != NULL)
     {
@@ -279,15 +288,17 @@ void __fastcall TProgressForm::UpdateControls()
   }
 
   int OverallProgress;
-  if (SynchronizeProgress != NULL)
+  // as a side effect this prevents calling TSynchronizeProgress::Progress when we do not know total size yet
+  // (what would cache wrong values forever)
+  if (TFileOperationProgressType::IsIndeterminateOperation(FData.Operation))
   {
-    OverallProgress = SynchronizeProgress->Progress();
+    OverallProgress = -1;
   }
   else
   {
-    if (IsIndeterminateOperation(FData.Operation))
+    if (SynchronizeProgress != NULL)
     {
-      OverallProgress = -1;
+      OverallProgress = SynchronizeProgress->Progress(&FData);
     }
     else
     {
@@ -296,7 +307,8 @@ void __fastcall TProgressForm::UpdateControls()
   }
   OperationProgress->Position = std::max(0, OverallProgress);
   OperationProgress->Hint = (OverallProgress < 0) ? UnicodeString() : FORMAT(L"%d%%", (OverallProgress));
-  Caption = FormatFormCaption(this, ProgressStr(SynchronizeProgress, &FData));
+  FProgressStr = ProgressStr(SynchronizeProgress, &FData);
+  Caption = FormatFormCaption(this, FProgressStr);
 
   if (TransferOperation)
   {
@@ -312,8 +324,16 @@ void __fastcall TProgressForm::UpdateControls()
     StartTimeLabel->Caption = FData.StartTime.TimeString();
     if (FData.TotalSizeSet)
     {
-      TimeLeftLabel->Caption = FormatDateTimeSpan(Configuration->TimeFormat,
-        FData.TotalTimeLeft());
+      TDateTime TimeLeft;
+      if (SynchronizeProgress != NULL)
+      {
+        TimeLeft = SynchronizeProgress->TimeLeft(&FData);
+      }
+      else
+      {
+        TimeLeft = FData.TotalTimeLeft();
+      }
+      TimeLeftLabel->Caption = FormatDateTimeSpan(Configuration->TimeFormat, TimeLeft);
     }
     TimeElapsedLabel->Caption = FormatDateTimeSpan(Configuration->TimeFormat, FData.TimeElapsed());
     BytesTransferredLabel->Caption = FormatBytes(FData.TotalTransferred);

+ 2 - 2
source/forms/Progress.h

@@ -112,6 +112,7 @@ private:
   TOnceDoneItems FOnceDoneItems;
   bool FAllowSkip;
   TSynchronizeProgress * FSynchronizeProgress;
+  UnicodeString FProgressStr;
 
   void __fastcall SetOnceDoneOperation(TOnceDoneOperation value);
   TTBCustomItem * __fastcall CurrentOnceDoneItem();
@@ -133,8 +134,6 @@ protected:
   virtual void __fastcall Dispatch(void * Message);
   void __fastcall SetCancelLower(TCancelStatus ACancel);
 
-  static bool __fastcall IsIndeterminateOperation(TFileOperation Operation);
-
 public:
   static UnicodeString __fastcall ProgressStr(
     const TSynchronizeProgress * SynchronizeProgress, const TFileOperationProgressType * ProgressData);
@@ -144,6 +143,7 @@ public:
   virtual __fastcall ~TProgressForm();
   void __fastcall SetProgressData(TFileOperationProgressType & AData);
   void __fastcall ClearCancel();
+  UnicodeString __fastcall ProgressStr();
   __property TCancelStatus Cancel = { read = FCancel };
   __property bool MoveToQueue = { read = FMoveToQueue };
   __property TOnceDoneOperation OnceDoneOperation = { read=GetOnceDoneOperation, write=SetOnceDoneOperation };

+ 3 - 11
source/windows/TerminalManager.cpp

@@ -680,13 +680,14 @@ void __fastcall TTerminalManager::UpdateAppTitle()
     UnicodeString NewTitle = FormatMainFormCaption(GetActiveTerminalTitle(false));
 
     UnicodeString QueueProgressTitle;
+    UnicodeString ProgressTitle = !FProgressTitle.IsEmpty() ? FProgressTitle : ScpExplorer->GetProgressTitle();
     if (!FForegroundProgressTitle.IsEmpty())
     {
       NewTitle = FForegroundProgressTitle + L" - " + NewTitle;
     }
-    else if (!FProgressTitle.IsEmpty() && !ForegroundTask())
+    else if (!ProgressTitle.IsEmpty() && !ForegroundTask())
     {
-      NewTitle = FProgressTitle + L" - " + NewTitle;
+      NewTitle = ProgressTitle + L" - " + NewTitle;
     }
     else if (ShouldDisplayQueueStatusOnAppTitle() &&
              !(QueueProgressTitle = ScpExplorer->GetQueueProgressTitle()).IsEmpty())
@@ -1191,15 +1192,6 @@ void __fastcall TTerminalManager::OperationFinished(::TFileOperation Operation,
 void __fastcall TTerminalManager::OperationProgress(
   TFileOperationProgressType & ProgressData)
 {
-  if (ProgressData.InProgress)
-  {
-    FProgressTitle = ScpExplorer->GetProgressTitle(ProgressData);
-  }
-  else
-  {
-    FProgressTitle = L"";
-  }
-
   UpdateAppTitle();
   DebugAssert(ScpExplorer);
   ScpExplorer->OperationProgress(ProgressData);