Procházet zdrojové kódy

Estimating time to finish directory comparison before synchronization based on previous comparisons of the same folders

Source commit: 01df1bed1a6df6d014df2151b6c9c08d3d860eb8
Martin Prikryl před 6 roky
rodič
revize
a62f0274e4

+ 59 - 0
source/core/Configuration.cpp

@@ -13,6 +13,7 @@
 #include "CoreMain.h"
 #include "Security.h"
 #include "FileMasks.h"
+#include "CopyParam.h"
 #include <shlobj.h>
 #include <System.IOUtils.hpp>
 #include <System.StrUtils.hpp>
@@ -1443,6 +1444,64 @@ void __fastcall TConfiguration::SetRandomSeedFile(UnicodeString value)
     }
   }
 }
+//---------------------------------------------------------------------------
+UnicodeString __fastcall TConfiguration::GetDirectoryStatisticsCacheKey(
+  const UnicodeString & SessionKey, const UnicodeString & Path, const TCopyParamType & CopyParam)
+{
+  std::unique_ptr<TStringList> RawOptions(new TStringList());
+  RawOptions->Add(SessionKey);
+  RawOptions->Add(UnixExcludeTrailingBackslash(Path));
+
+  TCopyParamType Defaults;
+  TCopyParamType FilterCopyParam;
+  FilterCopyParam.IncludeFileMask = CopyParam.IncludeFileMask;
+  FilterCopyParam.ExcludeHiddenFiles = CopyParam.ExcludeHiddenFiles;
+  FilterCopyParam.ExcludeEmptyDirectories = CopyParam.ExcludeEmptyDirectories;
+
+  std::unique_ptr<TOptionsStorage> OptionsStorage(new TOptionsStorage(RawOptions.get(), true));
+  FilterCopyParam.Save(OptionsStorage.get(), &Defaults);
+
+  UTF8String RawOptionsBuf(RawOptions->CommaText.LowerCase());
+  UnicodeString Result = Sha256(RawOptionsBuf.c_str(), RawOptionsBuf.Length());
+  return Result;
+}
+//---------------------------------------------------------------------------
+THierarchicalStorage * TConfiguration::OpenDirectoryStatisticsCache(bool CanCreate)
+{
+  std::unique_ptr<THierarchicalStorage> Storage(Configuration->CreateConfigStorage());
+  Storage->AccessMode = CanCreate ? smReadWrite : smRead;
+  if (!Storage->OpenSubKey(L"DirectoryStatisticsCache", CanCreate))
+  {
+    Storage.reset(NULL);
+  }
+  return Storage.release();
+}
+//---------------------------------------------------------------------------
+TStrings * __fastcall TConfiguration::LoadDirectoryStatisticsCache(
+  const UnicodeString & SessionKey, const UnicodeString & Path, const TCopyParamType & CopyParam)
+{
+  std::unique_ptr<THierarchicalStorage> Storage(OpenDirectoryStatisticsCache(false));
+  std::unique_ptr<TStringList> Result(new TStringList());
+  if (Storage.get() != NULL)
+  {
+    UnicodeString Key = GetDirectoryStatisticsCacheKey(SessionKey, Path, CopyParam);
+    UnicodeString Buf = Storage->ReadString(Key, UnicodeString());
+    Result->CommaText = Buf;
+  }
+  return Result.release();
+}
+//---------------------------------------------------------------------------
+void __fastcall TConfiguration::SaveDirectoryStatisticsCache(
+  const UnicodeString & SessionKey, const UnicodeString & Path, const TCopyParamType & CopyParam, TStrings * DataList)
+{
+  std::unique_ptr<THierarchicalStorage> Storage(OpenDirectoryStatisticsCache(true));
+  if (Storage.get() != NULL)
+  {
+    UnicodeString Key = GetDirectoryStatisticsCacheKey(SessionKey, Path, CopyParam);
+    UnicodeString Buf = DataList->CommaText;
+    Storage->WriteString(Key, Buf);
+  }
+}
 //---------------------------------------------------------------------
 UnicodeString __fastcall TConfiguration::GetRandomSeedFileName()
 {

+ 8 - 0
source/core/Configuration.h

@@ -18,6 +18,7 @@ extern const wchar_t * NotAutoSwitchNames;
 enum TAutoSwitch { asOn, asOff, asAuto }; // Has to match PuTTY FORCE_ON, FORCE_OFF, AUTO
 //---------------------------------------------------------------------------
 class TStoredSessionList;
+class TCopyParamType;
 //---------------------------------------------------------------------------
 class TConfiguration : public TObject
 {
@@ -169,6 +170,9 @@ protected:
   static UnicodeString __fastcall PropertyToKey(const UnicodeString & Property);
   virtual void __fastcall DoSave(bool All, bool Explicit);
   UnicodeString __fastcall FormatFingerprintKey(const UnicodeString & SiteKey, const UnicodeString & FingerprintType);
+  THierarchicalStorage * OpenDirectoryStatisticsCache(bool CanCreate);
+  UnicodeString __fastcall GetDirectoryStatisticsCacheKey(
+    const UnicodeString & SessionKey, const UnicodeString & Path, const TCopyParamType & CopyParam);
 
   virtual bool __fastcall GetConfirmOverwriting();
   virtual void __fastcall SetConfirmOverwriting(bool value);
@@ -227,6 +231,10 @@ public:
     TRemoteDirectoryChangesCache * DirectoryChangesCache);
   void __fastcall SaveDirectoryChangesCache(const UnicodeString SessionKey,
     TRemoteDirectoryChangesCache * DirectoryChangesCache);
+  TStrings * __fastcall LoadDirectoryStatisticsCache(
+    const UnicodeString & SessionKey, const UnicodeString & Path, const TCopyParamType & CopyParam);
+  void __fastcall SaveDirectoryStatisticsCache(
+    const UnicodeString & SessionKey, const UnicodeString & Path, const TCopyParamType & CopyParam, TStrings * DataList);
   bool __fastcall ShowBanner(const UnicodeString & SessionKey, const UnicodeString & Banner, unsigned int & Params);
   void __fastcall NeverShowBanner(const UnicodeString & SessionKey, const UnicodeString & Banner);
   void __fastcall SetBannerParams(const UnicodeString & SessionKey, unsigned int Params);

+ 2 - 2
source/core/Script.cpp

@@ -2394,8 +2394,8 @@ void __fastcall TManagementScript::TerminalOperationFinished(
 }
 //---------------------------------------------------------------------------
 void __fastcall TManagementScript::TerminalSynchronizeDirectory(
-  const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
-  bool & Continue, bool Collect)
+  const UnicodeString & LocalDirectory, const UnicodeString & RemoteDirectory,
+  bool & Continue, bool Collect, const TSynchronizeOptions *)
 {
   int SynchronizeMode = FSynchronizeMode;
   if (FKeepingUpToDate)

+ 3 - 2
source/core/Script.h

@@ -236,8 +236,9 @@ protected:
   void __fastcall FreeTerminal(TTerminal * Terminal);
   void __fastcall PrintProgress(bool First, const UnicodeString Str);
   bool __fastcall QueryCancel();
-  void __fastcall TerminalSynchronizeDirectory(const UnicodeString LocalDirectory,
-    const UnicodeString RemoteDirectory, bool & Continue, bool Collect);
+  void __fastcall TerminalSynchronizeDirectory(
+    const UnicodeString & LocalDirectory, const UnicodeString & RemoteDirectory,
+    bool & Continue, bool Collect, const TSynchronizeOptions * Options);
   void __fastcall DoChangeLocalDirectory(UnicodeString Directory);
   void __fastcall DoClose(TTerminal * Terminal);
   virtual bool __fastcall HandleExtendedException(Exception * E,

+ 7 - 2
source/core/Terminal.cpp

@@ -5760,6 +5760,11 @@ void __fastcall TTerminal::DoSynchronizeCollectFile(const UnicodeString FileName
 {
   TSynchronizeData * Data = static_cast<TSynchronizeData *>(Param);
 
+  if (Data->Options != NULL)
+  {
+    Data->Options->Files++;
+  }
+
   UnicodeString LocalFileName = ChangeFileName(Data->CopyParam, File->FileName, osRemote, false);
   UnicodeString FullRemoteFileName = UnixExcludeTrailingBackslash(File->FullFileName);
   if (DoAllowRemoteFileTransfer(File, Data->CopyParam, true) &&
@@ -6211,8 +6216,8 @@ void __fastcall TTerminal::DoSynchronizeProgress(const TSynchronizeData & Data,
   if (Data.OnSynchronizeDirectory != NULL)
   {
     bool Continue = true;
-    Data.OnSynchronizeDirectory(Data.LocalDirectory, Data.RemoteDirectory,
-      Continue, Collect);
+    Data.OnSynchronizeDirectory(
+      Data.LocalDirectory, Data.RemoteDirectory, Continue, Collect, Data.Options);
 
     if (!Continue)
     {

+ 3 - 2
source/core/Terminal.h

@@ -53,8 +53,8 @@ typedef void __fastcall (__closure *TProcessFileEventEx)
 typedef int __fastcall (__closure *TFileOperationEvent)
   (void * Param1, void * Param2);
 typedef void __fastcall (__closure *TSynchronizeDirectory)
-  (const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
-   bool & Continue, bool Collect);
+  (const UnicodeString & LocalDirectory, const UnicodeString & RemoteDirectory,
+   bool & Continue, bool Collect, const TSynchronizeOptions * Options);
 typedef void __fastcall (__closure *TUpdatedSynchronizationChecklistItems)(
   const TSynchronizeChecklist::TItemList & Items);
 typedef void __fastcall (__closure *TProcessedSynchronizationChecklistItem)(
@@ -763,6 +763,7 @@ struct TSynchronizeOptions
   ~TSynchronizeOptions();
 
   TStringList * Filter;
+  int Files;
 
   bool __fastcall FilterFind(const UnicodeString & FileName);
   bool __fastcall MatchesFilter(const UnicodeString & FileName);

+ 47 - 20
source/forms/CustomScpExplorer.cpp

@@ -850,23 +850,11 @@ void __fastcall TCustomScpExplorerForm::SetTaskbarListProgressState(TBPFLAG Flag
   FTaskbarList->SetProgressState(GetMainForm()->Handle, Flags);
 }
 //---------------------------------------------------------------------------
-void __fastcall TCustomScpExplorerForm::SetTaskbarListProgressValue(TFileOperationProgressType * ProgressData)
+void __fastcall TCustomScpExplorerForm::SetTaskbarListProgressValue(int Progress)
 {
-  if (!TFileOperationProgressType::IsIndeterminateOperation(ProgressData->Operation))
+  if (Progress >= 0)
   {
-    int OverallProgress;
-    // FProgressForm is null when this is called from SetQueueProgress
-    if ((FProgressForm != NULL) && (FProgressForm->SynchronizeProgress != NULL))
-    {
-      OverallProgress = FProgressForm->SynchronizeProgress->Progress(ProgressData);
-    }
-    else
-    {
-      OverallProgress = ProgressData->OverallProgress();
-    }
-
-    // implies TBPF_NORMAL
-    FTaskbarList->SetProgressValue(GetMainForm()->Handle, OverallProgress, 100);
+    FTaskbarList->SetProgressValue(GetMainForm()->Handle, Progress, 100);
   }
   else
   {
@@ -874,6 +862,25 @@ void __fastcall TCustomScpExplorerForm::SetTaskbarListProgressValue(TFileOperati
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::SetTaskbarListProgressValue(TFileOperationProgressType * ProgressData)
+{
+  int OverallProgress;
+  // FProgressForm is null when this is called from SetQueueProgress
+  if ((FProgressForm != NULL) && (FProgressForm->SynchronizeProgress != NULL))
+  {
+    OverallProgress = FProgressForm->SynchronizeProgress->Progress(ProgressData);
+  }
+  else if (!TFileOperationProgressType::IsIndeterminateOperation(ProgressData->Operation))
+  {
+    OverallProgress = ProgressData->OverallProgress();
+  }
+  else
+  {
+    OverallProgress = -1;
+  }
+  SetTaskbarListProgressValue(OverallProgress);
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::SetQueueProgress()
 {
   TTerminalManager::Instance()->QueueStatusUpdated();
@@ -5417,7 +5424,7 @@ void __fastcall TCustomScpExplorerForm::Synchronize(const UnicodeString LocalDir
   TSynchronizeChecklist * AChecklist = NULL;
   try
   {
-    FSynchronizeProgressForm = new TSynchronizeProgressForm(Application, true);
+    FSynchronizeProgressForm = new TSynchronizeProgressForm(Application, true, -1);
     if (FLAGCLEAR(Params, TTerminal::spDelayProgress))
     {
       FSynchronizeProgressForm->Start();
@@ -5771,13 +5778,32 @@ int __fastcall TCustomScpExplorerForm::DoFullSynchronizeDirectories(
 
       try
       {
-        FSynchronizeProgressForm = new TSynchronizeProgressForm(Application, true);
+        UnicodeString SessionKey = Terminal->SessionData->SessionKey;
+        std::unique_ptr<TStrings> DataList(Configuration->LoadDirectoryStatisticsCache(SessionKey, RemoteDirectory, CopyParam));
+
+        int Files = -1;
+        if (DataList->Count >= 1)
+        {
+          Files = StrToIntDef(DataList->Strings[0], Files);
+        }
+        else
+        {
+          DataList->Add(UnicodeString());
+        }
+
+        FSynchronizeProgressForm = new TSynchronizeProgressForm(Application, true, Files);
         FSynchronizeProgressForm->Start();
 
         Checklist = Terminal->SynchronizeCollect(LocalDirectory, RemoteDirectory,
           static_cast<TTerminal::TSynchronizeMode>(Mode),
           &CopyParam, Params | TTerminal::spNoConfirmation, TerminalSynchronizeDirectory,
           &SynchronizeOptions);
+
+        if (Terminal->SessionData->CacheDirectories)
+        {
+          DataList->Strings[0] = IntToStr(SynchronizeOptions.Files);
+          Configuration->SaveDirectoryStatisticsCache(SessionKey, RemoteDirectory, CopyParam, DataList.get());
+        }
       }
       __finally
       {
@@ -5834,8 +5860,8 @@ int __fastcall TCustomScpExplorerForm::DoFullSynchronizeDirectories(
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::TerminalSynchronizeDirectory(
-  const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
-  bool & Continue, bool Collect)
+  const UnicodeString & LocalDirectory, const UnicodeString & RemoteDirectory,
+  bool & Continue, bool Collect, const TSynchronizeOptions * Options)
 {
   if (Collect)
   {
@@ -5844,7 +5870,8 @@ void __fastcall TCustomScpExplorerForm::TerminalSynchronizeDirectory(
     {
       FSynchronizeProgressForm->Start();
     }
-    FSynchronizeProgressForm->SetData(LocalDirectory, RemoteDirectory, Continue);
+    int CompareProgress = FSynchronizeProgressForm->SetData(LocalDirectory, RemoteDirectory, Options->Files, Continue);
+    SetTaskbarListProgressValue(CompareProgress);
   }
   else
   {

+ 4 - 2
source/forms/CustomScpExplorer.h

@@ -438,8 +438,9 @@ protected:
   DYNAMIC void __fastcall DoShow();
   TStrings * __fastcall CreateVisitedDirectories(TOperationSide Side);
   void __fastcall HandleErrorList(TStringList *& ErrorList);
-  void __fastcall TerminalSynchronizeDirectory(const UnicodeString LocalDirectory,
-    const UnicodeString RemoteDirectory, bool & Continue, bool Collect);
+  void __fastcall TerminalSynchronizeDirectory(
+    const UnicodeString & LocalDirectory, const UnicodeString & RemoteDirectory,
+    bool & Continue, bool Collect, const TSynchronizeOptions * Options);
   void __fastcall DoSynchronize(TSynchronizeController * Sender,
     const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
     const TCopyParamType & CopyParam, const TSynchronizeParamType & Params,
@@ -623,6 +624,7 @@ protected:
   void __fastcall SetQueueProgress();
   void __fastcall UpdateQueueLabel();
   void __fastcall SetTaskbarListProgressState(TBPFLAG Flags);
+  void __fastcall SetTaskbarListProgressValue(int Progress);
   void __fastcall SetTaskbarListProgressValue(TFileOperationProgressType * ProgressData);
   TTerminal * __fastcall GetSessionTabTerminal(TTabSheet * TabSheet);
   bool __fastcall SessionTabSwitched();

+ 1 - 1
source/forms/Progress.cpp

@@ -325,7 +325,7 @@ void __fastcall TProgressForm::UpdateControls()
     if (FData.TotalSizeSet)
     {
       UnicodeString TimeLeftCaption;
-      if (SecondsBetween(FData.StartTime, Now()) >= 3)
+      if (CanShowTimeEstimate(FData.StartTime))
       {
         TDateTime TimeLeft;
         if (SynchronizeProgress != NULL)

+ 45 - 7
source/forms/SynchronizeProgress.cpp

@@ -23,14 +23,20 @@
 #endif
 //---------------------------------------------------------------------------
 // Used for comparing only
-__fastcall TSynchronizeProgressForm::TSynchronizeProgressForm(TComponent * Owner, bool AllowMinimize)
+__fastcall TSynchronizeProgressForm::TSynchronizeProgressForm(TComponent * Owner, bool AllowMinimize, int Files)
   : TForm(Owner)
 {
   FStarted = false;
   FCanceled = false;
-  FElapsed = EncodeTimeVerbose(0, 0, 0, 0);
   FShowAsModalStorage = NULL;
   FMinimizedByMe = false;
+  bool KnowsTotalFiles = (Files >= 0);
+  OperationProgress->Style = (KnowsTotalFiles ? pbstNormal : pbstMarquee);
+  OperationProgress->Max = (KnowsTotalFiles ? Files : 1);
+  TimeLeftLabelLabel->Visible = KnowsTotalFiles;
+  TimeLeftLabel->Visible = KnowsTotalFiles;
+  StartTimeLabelLabel->Visible = !KnowsTotalFiles;
+  StartTimeLabel->Visible = !KnowsTotalFiles;
   UseSystemSettings(this);
   HideComponentsPanel(this);
   SelectScaledImageList(ImageList);
@@ -66,7 +72,8 @@ void __fastcall TSynchronizeProgressForm::Start()
   FStartTime = Now();
   UpdateTimer->Enabled = true;
   StartTimeLabel->Caption = FStartTime.TimeString();
-  Caption = FormatFormCaption(this, LoadStr(SYNCHRONIZE_PROGRESS_COMPARE));
+  OperationProgress->Position = OperationProgress->Min;
+  UpdateControls();
   if (!IsApplicationMinimized())
   {
     // Do not show the progress when the application is minimized,
@@ -82,25 +89,56 @@ void __fastcall TSynchronizeProgressForm::Start()
   FFrameAnimation.Start();
 }
 //---------------------------------------------------------------------------
-void __fastcall TSynchronizeProgressForm::SetData(const UnicodeString LocalDirectory,
-  const UnicodeString RemoteDirectory, bool & Continue)
+int __fastcall TSynchronizeProgressForm::SetData(
+  const UnicodeString & LocalDirectory, const UnicodeString & RemoteDirectory, int Progress, bool & Continue)
 {
   DebugAssert(FStarted);
   LocalDirectoryLabel->Caption = LocalDirectory;
   RemoteDirectoryLabel->Caption = RemoteDirectory;
+  OperationProgress->Position = Progress;
   Continue = !FCanceled;
 
   UpdateControls();
   Application->ProcessMessages();
+  return CalculateProgress();
+}
+//---------------------------------------------------------------------------
+int __fastcall TSynchronizeProgressForm::CalculateProgress()
+{
+  return ((OperationProgress->Style == pbstMarquee) ? -1 : ((OperationProgress->Position * 100) / OperationProgress->Max));
 }
 //---------------------------------------------------------------------------
 void __fastcall TSynchronizeProgressForm::UpdateControls()
 {
+  TDateTime Elapsed;
+  UnicodeString ACaption = FormatFormCaption(this, LoadStr(SYNCHRONIZE_PROGRESS_COMPARE));
   if (FStarted)
   {
-    FElapsed = Now() - FStartTime;
+    Elapsed = Now() - FStartTime;
+    int Progress = CalculateProgress();
+    if (Progress >= 0)
+    {
+      ACaption = FORMAT(L"%d%% %s", (Progress, ACaption));
+    }
+  }
+  else
+  {
+    Elapsed = EncodeTimeVerbose(0, 0, 0, 0);
+  }
+  Caption = ACaption;
+  TimeElapsedLabel->Caption = FormatDateTimeSpan(Configuration->TimeFormat, Elapsed);
+  UnicodeString TimeLeftCaption;
+  int Position = OperationProgress->Position;
+  if (FStarted && CanShowTimeEstimate(FStartTime) && (Position > 0))
+  {
+    TDateTime TimeLeft = TDateTime(double(double(Elapsed) * (OperationProgress->Max - Position) / Position));
+    TimeLeftCaption = FormatDateTimeSpan(Configuration->TimeFormat, TimeLeft);
+  }
+  else
+  {
+    TimeLeftCaption = LoadStr(PROGRESS_TIME_LEFT_CALCULATING);
   }
-  TimeElapsedLabel->Caption = FormatDateTimeSpan(Configuration->TimeFormat, FElapsed);
+  TimeLeftLabel->Caption = TimeLeftCaption;
   CancelItem->Enabled = !FCanceled;
 }
 //---------------------------------------------------------------------------

+ 37 - 10
source/forms/SynchronizeProgress.dfm

@@ -4,7 +4,7 @@ object SynchronizeProgressForm: TSynchronizeProgressForm
   BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
   BorderStyle = bsDialog
   Caption = 'Synchronization X'
-  ClientHeight = 193
+  ClientHeight = 219
   ClientWidth = 424
   Color = clWindow
   ParentFont = True
@@ -12,9 +12,26 @@ object SynchronizeProgressForm: TSynchronizeProgressForm
   Position = poOwnerFormCenter
   DesignSize = (
     424
-    193)
+    219)
   PixelsPerInch = 96
   TextHeight = 13
+  object TimeLeftLabel: TLabel
+    Left = 141
+    Top = 49
+    Width = 79
+    Height = 13
+    AutoSize = False
+    Caption = '00:00:00'
+    ShowAccelChar = False
+  end
+  object TimeLeftLabelLabel: TLabel
+    Left = 49
+    Top = 49
+    Width = 45
+    Height = 13
+    Caption = 'Time left:'
+    ShowAccelChar = False
+  end
   object Label1: TLabel
     Left = 49
     Top = 9
@@ -63,7 +80,7 @@ object SynchronizeProgressForm: TSynchronizeProgressForm
     Caption = '00:00:00'
     ShowAccelChar = False
   end
-  object Label4: TLabel
+  object StartTimeLabelLabel: TLabel
     Left = 49
     Top = 49
     Width = 51
@@ -96,7 +113,7 @@ object SynchronizeProgressForm: TSynchronizeProgressForm
   end
   object ToolbarPanel: TPanel
     Left = 49
-    Top = 92
+    Top = 115
     Width = 185
     Height = 28
     Anchors = [akLeft, akBottom]
@@ -138,7 +155,7 @@ object SynchronizeProgressForm: TSynchronizeProgressForm
   end
   object ComponentsPanel: TPanel
     Left = 0
-    Top = 127
+    Top = 153
     Width = 424
     Height = 66
     Align = alBottom
@@ -147,11 +164,21 @@ object SynchronizeProgressForm: TSynchronizeProgressForm
     BevelOuter = bvNone
     TabOrder = 1
   end
+  object OperationProgress: TProgressBar
+    Left = 49
+    Top = 88
+    Width = 365
+    Height = 16
+    Anchors = [akLeft, akTop, akRight]
+    ParentShowHint = False
+    ShowHint = True
+    TabOrder = 2
+  end
   object UpdateTimer: TTimer
     Enabled = False
     OnTimer = UpdateTimerTimer
     Left = 393
-    Top = 140
+    Top = 166
   end
   object ImageList: TPngImageList
     PngImages = <
@@ -353,7 +380,7 @@ object SynchronizeProgressForm: TSynchronizeProgressForm
           574C0000000049454E44AE426082}
       end>
     Left = 40
-    Top = 140
+    Top = 166
     Bitmap = {}
   end
   object ImageList120: TPngImageList
@@ -559,7 +586,7 @@ object SynchronizeProgressForm: TSynchronizeProgressForm
           02001CD843D9E9F4EAFC0000000049454E44AE426082}
       end>
     Left = 136
-    Top = 140
+    Top = 166
     Bitmap = {}
   end
   object ImageList144: TPngImageList
@@ -765,7 +792,7 @@ object SynchronizeProgressForm: TSynchronizeProgressForm
           5ED71FD84ED953D17B83BCD30000000049454E44AE426082}
       end>
     Left = 224
-    Top = 140
+    Top = 166
     Bitmap = {}
   end
   object ImageList192: TPngImageList
@@ -979,7 +1006,7 @@ object SynchronizeProgressForm: TSynchronizeProgressForm
           82}
       end>
     Left = 312
-    Top = 140
+    Top = 166
     Bitmap = {}
   end
 end

+ 9 - 5
source/forms/SynchronizeProgress.h

@@ -15,6 +15,7 @@
 #include "TBX.hpp"
 #include <Vcl.ImgList.hpp>
 #include <GUITools.h>
+#include <Vcl.ComCtrls.hpp>
 //---------------------------------------------------------------------------
 class TSynchronizeProgressForm : public TForm
 {
@@ -24,7 +25,7 @@ __published:
   TPathLabel *RemoteDirectoryLabel;
   TPathLabel *LocalDirectoryLabel;
   TLabel *StartTimeLabel;
-  TLabel *Label4;
+  TLabel *StartTimeLabelLabel;
   TLabel *Label3;
   TLabel *TimeElapsedLabel;
   TTimer *UpdateTimer;
@@ -39,26 +40,29 @@ __published:
   TPngImageList *ImageList120;
   TPngImageList *ImageList144;
   TPngImageList *ImageList192;
+  TProgressBar *OperationProgress;
+  TLabel *TimeLeftLabelLabel;
+  TLabel *TimeLeftLabel;
   void __fastcall UpdateTimerTimer(TObject *Sender);
   void __fastcall MinimizeItemClick(TObject *Sender);
   void __fastcall CancelItemClick(TObject *Sender);
 
 public:
-  __fastcall TSynchronizeProgressForm(TComponent * Owner, bool AllowMinimize);
+  __fastcall TSynchronizeProgressForm(TComponent * Owner, bool AllowMinimize, int Files);
   virtual __fastcall ~TSynchronizeProgressForm();
 
   void __fastcall Start();
-  void __fastcall SetData(const UnicodeString LocalDirectory,
-    const UnicodeString RemoteDirectory, bool & Continue);
+  int __fastcall SetData(
+    const UnicodeString & LocalDirectory, const UnicodeString & RemoteDirectory, int Progress, bool & Continue);
 
   __property bool Started = { read = FStarted };
 
 protected:
   virtual void __fastcall Dispatch(void * Message);
+  int __fastcall CalculateProgress();
 
 private:
   TDateTime FStartTime;
-  TDateTime FElapsed;
   bool FStarted;
   bool FCanceled;
   void * FShowAsModalStorage;

+ 6 - 0
source/windows/GUITools.cpp

@@ -27,6 +27,7 @@
 #include <Vcl.ScreenTips.hpp>
 #include <HistoryComboBox.hpp>
 #include <vssym32.h>
+#include <DateUtils.hpp>
 
 #include "Animations96.h"
 #include "Animations120.h"
@@ -2110,3 +2111,8 @@ void __fastcall FindComponentClass(
     ComponentClass = __classid(TUIStateAwareComboBox);
   }
 }
+//---------------------------------------------------------------------------
+bool CanShowTimeEstimate(TDateTime StartTime)
+{
+  return (SecondsBetween(StartTime, Now()) >= 3);
+}

+ 1 - 0
source/windows/GUITools.h

@@ -61,6 +61,7 @@ void LoadBrowserDocument(TWebBrowserEx * WebBrowser, const UnicodeString & Docum
 TComponent * __fastcall FindComponentRecursively(TComponent * Root, const UnicodeString & Name);
 void __fastcall GetInstrutionsTheme(
   TColor & MainInstructionColor, HFONT & MainInstructionFont, HFONT & InstructionFont);
+bool CanShowTimeEstimate(TDateTime StartTime);
 //---------------------------------------------------------------------------
 class TLocalCustomCommand : public TFileCustomCommand
 {