Browse Source

Issue 2318 – Optionally push synchronization actions to queue

https://winscp.net/tracker/2318

Source commit: 8d79009ebeabde5ce762a82d61f36e18c1517d9b
Martin Prikryl 10 months ago
parent
commit
cbd7e799df

+ 23 - 7
source/core/Queue.cpp

@@ -2281,10 +2281,18 @@ static void ExtractLocalSourcePath(TStrings * Files, UnicodeString & Path)
   Path = ExtractFileDir(IncludeTrailingBackslash(Path));
 }
 //---------------------------------------------------------------------------
-__fastcall TUploadQueueItem::TUploadQueueItem(TTerminal * Terminal,
-  TStrings * FilesToCopy, const UnicodeString & TargetDir,
-  const TCopyParamType * CopyParam, int Params, bool SingleFile, bool Parallel) :
-  TTransferQueueItem(Terminal, FilesToCopy, TargetDir, CopyParam, Params, osLocal, SingleFile, Parallel)
+static bool IsSingleFileUpload(TStrings * FilesToCopy)
+{
+  return
+    (FilesToCopy->Count == 1) &&
+    FileExists(ApiPath(FilesToCopy->Strings[0]));
+}
+//---------------------------------------------------------------------------
+__fastcall TUploadQueueItem::TUploadQueueItem(
+    TTerminal * Terminal, TStrings * FilesToCopy, const UnicodeString & TargetDir,
+    const TCopyParamType * CopyParam, int Params, bool Parallel) :
+  TTransferQueueItem(
+    Terminal, FilesToCopy, TargetDir, CopyParam, Params, osLocal, IsSingleFileUpload(FilesToCopy), Parallel)
 {
   if (FilesToCopy->Count > 1)
   {
@@ -2392,10 +2400,18 @@ static void ExtractRemoteSourcePath(TTerminal * Terminal, TStrings * Files, Unic
   Path = UnixExcludeTrailingBackslash(Path);
 }
 //---------------------------------------------------------------------------
+static bool IsSingleFileDownload(TStrings * FilesToCopy)
+{
+  return
+    (FilesToCopy->Count == 1) &&
+    !static_cast<TRemoteFile *>(FilesToCopy->Objects[0])->IsDirectory;
+}
+//---------------------------------------------------------------------------
 __fastcall TDownloadQueueItem::TDownloadQueueItem(TTerminal * Terminal,
-  TStrings * FilesToCopy, const UnicodeString & TargetDir,
-  const TCopyParamType * CopyParam, int Params, bool SingleFile, bool Parallel) :
-  TTransferQueueItem(Terminal, FilesToCopy, TargetDir, CopyParam, Params, osRemote, SingleFile, Parallel)
+    TStrings * FilesToCopy, const UnicodeString & TargetDir,
+    const TCopyParamType * CopyParam, int Params, bool Parallel) :
+  TTransferQueueItem(
+    Terminal, FilesToCopy, TargetDir, CopyParam, Params, osRemote, IsSingleFileDownload(FilesToCopy), Parallel)
 {
   if (FilesToCopy->Count > 1)
   {

+ 2 - 2
source/core/Queue.h

@@ -359,7 +359,7 @@ class TUploadQueueItem : public TTransferQueueItem
 public:
   __fastcall TUploadQueueItem(TTerminal * Terminal,
     TStrings * FilesToCopy, const UnicodeString & TargetDir,
-    const TCopyParamType * CopyParam, int Params, bool SingleFile, bool Parallel);
+    const TCopyParamType * CopyParam, int Params, bool Parallel);
 
 protected:
   virtual void __fastcall DoTransferExecute(TTerminal * Terminal, TParallelOperation * ParallelOperation);
@@ -370,7 +370,7 @@ class TDownloadQueueItem : public TTransferQueueItem
 public:
   __fastcall TDownloadQueueItem(TTerminal * Terminal,
     TStrings * FilesToCopy, const UnicodeString & TargetDir,
-    const TCopyParamType * CopyParam, int Params, bool SingleFile, bool Parallel);
+    const TCopyParamType * CopyParam, int Params, bool Parallel);
 
 protected:
   virtual void __fastcall DoTransferExecute(TTerminal * Terminal, TParallelOperation * ParallelOperation);

+ 35 - 0
source/core/RemoteFiles.cpp

@@ -2923,6 +2923,41 @@ UnicodeString TSynchronizeChecklist::TItem::GetRemotePath() const
   return UnixCombinePaths(Remote.Directory, Remote.FileName);
 }
 //---------------------------------------------------------------------------
+UnicodeString TSynchronizeChecklist::TItem::GetLocalTarget() const
+{
+  return IncludeTrailingBackslash(Local.Directory);
+}
+//---------------------------------------------------------------------------
+UnicodeString TSynchronizeChecklist::TItem::GetRemoteTarget() const
+{
+  return UnixIncludeTrailingBackslash(Remote.Directory);
+};
+//---------------------------------------------------------------------------
+TStrings * TSynchronizeChecklist::TItem::GetFileList() const
+{
+  std::unique_ptr<TStrings> FileList(new TStringList());
+  switch (Action)
+  {
+    case TSynchronizeChecklist::saDownloadNew:
+    case TSynchronizeChecklist::saDownloadUpdate:
+    case TSynchronizeChecklist::saDeleteRemote:
+      FileList->AddObject(GetRemotePath(), RemoteFile);
+      break;
+
+    case TSynchronizeChecklist::saUploadNew:
+    case TSynchronizeChecklist::saUploadUpdate:
+    case TSynchronizeChecklist::saDeleteLocal:
+      FileList->Add(GetLocalPath());
+      break;
+
+    default:
+      DebugFail();
+      NotImplemented();
+      break;
+  }
+  return FileList.release();
+}
+//---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 TSynchronizeChecklist::TSynchronizeChecklist() :
   FList(new TList())

+ 3 - 0
source/core/RemoteFiles.h

@@ -502,6 +502,9 @@ public:
     UnicodeString GetLocalPath() const;
     // Contrary to RemoteFile->FullFileName, this does not include trailing slash for directories
     UnicodeString GetRemotePath() const;
+    UnicodeString GetLocalTarget() const;
+    UnicodeString GetRemoteTarget() const;
+    TStrings * GetFileList() const;
 
     ~TItem();
 

+ 73 - 26
source/core/Terminal.cpp

@@ -6558,6 +6558,65 @@ void __fastcall TTerminal::DoSynchronizeCollectFile(const UnicodeString FileName
   }
 }
 //---------------------------------------------------------------------------
+TCopyParamType TTerminal::GetSynchronizeCopyParam(const TCopyParamType * CopyParam, int Params)
+{
+  TCopyParamType SyncCopyParam = *CopyParam;
+  // when synchronizing by time, we force preserving time,
+  // otherwise it does not make any sense
+  if (FLAGCLEAR(Params, spNotByTime))
+  {
+    SyncCopyParam.PreserveTime = true;
+  }
+  return SyncCopyParam;
+}
+//---------------------------------------------------------------------------
+int TTerminal::GetSynchronizeCopyParams(int Params)
+{
+  return
+    // The spNoConfirmation seems to always be present
+    FLAGMASK(DebugAlwaysTrue(FLAGSET(Params, spNoConfirmation)), cpNoConfirmation);
+}
+//---------------------------------------------------------------------------
+TQueueItem * TTerminal::SynchronizeToQueue(
+  const TSynchronizeChecklist::TItem * ChecklistItem, const TCopyParamType * CopyParam, int Params, bool Parallel)
+{
+  TQueueItem * Result;
+  if (DebugAlwaysFalse(FLAGSET(Params, spTimestamp)))
+  {
+    NotImplemented();
+  }
+  else
+  {
+    std::unique_ptr<TStrings> FileList(ChecklistItem->GetFileList());
+    switch (ChecklistItem->Action)
+    {
+      case TSynchronizeChecklist::saDownloadNew:
+      case TSynchronizeChecklist::saDownloadUpdate:
+        Result = new TDownloadQueueItem(this, FileList.get(), ChecklistItem->GetLocalTarget(), CopyParam, Params, Parallel);
+        break;
+
+      case TSynchronizeChecklist::saDeleteRemote:
+        Result = new TRemoteDeleteQueueItem(this, FileList.get(), 0);
+        break;
+
+      case TSynchronizeChecklist::saUploadNew:
+      case TSynchronizeChecklist::saUploadUpdate:
+        Result = new TUploadQueueItem(this, FileList.get(), ChecklistItem->GetRemoteTarget(), CopyParam, Params, Parallel);
+        break;
+
+      case TSynchronizeChecklist::saDeleteLocal:
+        Result = new TLocalDeleteQueueItem(FileList.get(), 0);
+        break;
+
+      default:
+        DebugFail();
+        NotImplemented();
+        break;
+    }
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 void __fastcall TTerminal::SynchronizeApply(
   TSynchronizeChecklist * Checklist,
   const TCopyParamType * CopyParam, int Params,
@@ -6569,16 +6628,8 @@ void __fastcall TTerminal::SynchronizeApply(
 
   Data.OnSynchronizeDirectory = OnSynchronizeDirectory;
 
-  int CopyParams =
-    FLAGMASK(FLAGSET(Params, spNoConfirmation), cpNoConfirmation);
-
-  TCopyParamType SyncCopyParam = *CopyParam;
-  // when synchronizing by time, we force preserving time,
-  // otherwise it does not make any sense
-  if (FLAGCLEAR(Params, spNotByTime))
-  {
-    SyncCopyParam.PreserveTime = true;
-  }
+  int CopyParams = GetSynchronizeCopyParams(Params);
+  TCopyParamType SyncCopyParam = GetSynchronizeCopyParam(CopyParam, Params);
 
   if (SyncCopyParam.CalculateSize)
   {
@@ -6619,11 +6670,13 @@ void __fastcall TTerminal::SynchronizeApply(
     const TSynchronizeChecklist::TItem * ChecklistItem;
     while (Checklist->GetNextChecked(Index, ChecklistItem))
     {
-      if (!SamePaths(Data.LocalDirectory, ChecklistItem->Local.Directory) ||
-          !UnixSamePath(Data.RemoteDirectory, ChecklistItem->Remote.Directory))
+      UnicodeString LocalTarget = ChecklistItem->GetLocalTarget();
+      UnicodeString RemoteTarget = ChecklistItem->GetRemoteTarget();
+      if (!SamePaths(Data.LocalDirectory, LocalTarget) ||
+          !UnixSamePath(Data.RemoteDirectory, RemoteTarget))
       {
-        Data.LocalDirectory = IncludeTrailingBackslash(ChecklistItem->Local.Directory);
-        Data.RemoteDirectory = UnixIncludeTrailingBackslash(ChecklistItem->Remote.Directory);
+        Data.LocalDirectory = LocalTarget;
+        Data.RemoteDirectory = RemoteTarget;
 
         LogEvent(
           FORMAT(L"Synchronizing local directory '%s' with remote directory '%s', params = 0x%x (%s)",
@@ -6632,25 +6685,22 @@ void __fastcall TTerminal::SynchronizeApply(
         DoSynchronizeProgress(Data, false);
       }
 
-      std::unique_ptr<TStringList> FileList(new TStringList());
-
-      UnicodeString LocalPath = ChecklistItem->GetLocalPath();
-      UnicodeString RemotePath = ChecklistItem->GetRemotePath();
       bool Result = true;
 
       if (FLAGSET(Params, spTimestamp))
       {
         // used by SynchronizeLocalTimestamp and SynchronizeRemoteTimestamp
         TObject * ChecklistItemToken = const_cast<TObject *>(reinterpret_cast<const TObject *>(ChecklistItem));
+        std::unique_ptr<TStringList> FileList(new TStringList());
         switch (ChecklistItem->Action)
         {
           case TSynchronizeChecklist::saDownloadUpdate:
-            FileList->AddObject(RemotePath, ChecklistItemToken);
+            FileList->AddObject(ChecklistItem->GetRemotePath(), ChecklistItemToken);
             ProcessFiles(FileList.get(), foSetProperties, SynchronizeLocalTimestamp, NULL, osLocal);
             break;
 
           case TSynchronizeChecklist::saUploadUpdate:
-            FileList->AddObject(LocalPath, ChecklistItemToken);
+            FileList->AddObject(ChecklistItem->GetLocalPath(), ChecklistItemToken);
             ProcessFiles(FileList.get(), foSetProperties, SynchronizeRemoteTimestamp);
             break;
 
@@ -6662,29 +6712,26 @@ void __fastcall TTerminal::SynchronizeApply(
       }
       else
       {
+        std::unique_ptr<TStrings> FileList(ChecklistItem->GetFileList());
         TCopyParamType ItemCopyParam = SyncCopyParam;
         ItemCopyParam.Size = ChecklistItem->HasSize() ? ChecklistItem->GetSize() : -1;
         switch (ChecklistItem->Action)
         {
           case TSynchronizeChecklist::saDownloadNew:
           case TSynchronizeChecklist::saDownloadUpdate:
-            FileList->AddObject(RemotePath, ChecklistItem->RemoteFile);
-            Result = CopyToLocal(FileList.get(), Data.LocalDirectory, &ItemCopyParam, CopyParams, NULL);
+            Result = CopyToLocal(FileList.get(), LocalTarget, &ItemCopyParam, CopyParams, NULL);
             break;
 
           case TSynchronizeChecklist::saDeleteRemote:
-            FileList->AddObject(RemotePath, ChecklistItem->RemoteFile);
             Result = DeleteFiles(FileList.get());
             break;
 
           case TSynchronizeChecklist::saUploadNew:
           case TSynchronizeChecklist::saUploadUpdate:
-            FileList->Add(LocalPath);
-            Result = CopyToRemote(FileList.get(), Data.RemoteDirectory, &ItemCopyParam, CopyParams, NULL);
+            Result = CopyToRemote(FileList.get(), RemoteTarget, &ItemCopyParam, CopyParams, NULL);
             break;
 
           case TSynchronizeChecklist::saDeleteLocal:
-            FileList->Add(LocalPath);
             Result = DeleteLocalFiles(FileList.get());
             break;
 

+ 5 - 0
source/core/Terminal.h

@@ -31,6 +31,7 @@ class TParallelOperation;
 class TCollectedFileList;
 struct TLocalFileHandle;
 struct TNeonCertificateData;
+class TQueueItem;
 typedef std::vector<__int64> TCalculatedSizes;
 //---------------------------------------------------------------------------
 typedef void __fastcall (__closure *TQueryUserEvent)
@@ -613,9 +614,13 @@ public:
     TSynchronizeDirectory OnSynchronizeDirectory, TProcessedSynchronizationChecklistItem OnProcessedItem,
     TUpdatedSynchronizationChecklistItems OnUpdatedSynchronizationChecklistItems, void * Token,
     TFileOperationStatistics * Statistics);
+  TQueueItem * SynchronizeToQueue(
+    const TSynchronizeChecklist::TItem * ChecklistItem, const TCopyParamType * CopyParam, int Params, bool Parallel);
   void __fastcall SynchronizeChecklistCalculateSize(
     TSynchronizeChecklist * Checklist, const TSynchronizeChecklist::TItemList & Items,
     const TCopyParamType * CopyParam);
+  static TCopyParamType GetSynchronizeCopyParam(const TCopyParamType * CopyParam, int Params);
+  static int GetSynchronizeCopyParams(int Params);
   void __fastcall FilesFind(UnicodeString Directory, const TFileMasks & FileMask,
     TFileFoundEvent OnFileFound, TFindingFileEvent OnFindingFile);
   void __fastcall SpaceAvailable(const UnicodeString Path, TSpaceAvailable & ASpaceAvailable);

+ 32 - 21
source/forms/CustomScpExplorer.cpp

@@ -1392,33 +1392,20 @@ void __fastcall TCustomScpExplorerForm::AddQueueItem(
 {
   DebugAssert(Queue != NULL);
 
-  bool SingleFile = false;
-  if (FileList->Count == 1)
-  {
-    if (Direction == tdToRemote)
-    {
-      UnicodeString FileName = FileList->Strings[0];
-      SingleFile = FileExists(ApiPath(FileName));
-    }
-    else
-    {
-      TRemoteFile * File = static_cast<TRemoteFile *>(FileList->Objects[0]);
-      SingleFile = !File->IsDirectory;
-    }
-  }
-
   TQueueItem * QueueItem;
   if (Direction == tdToRemote)
   {
     CopyParam.IncludeFileMask.SetRoots(FileList, TargetDirectory);
-    QueueItem = new TUploadQueueItem(Terminal, FileList, TargetDirectory,
-      &CopyParam, Params, SingleFile, CopyParam.QueueParallel);
+    QueueItem =
+      new TUploadQueueItem(
+        Terminal, FileList, TargetDirectory, &CopyParam, Params, CopyParam.QueueParallel);
   }
   else
   {
     CopyParam.IncludeFileMask.SetRoots(TargetDirectory, FileList);
-    QueueItem = new TDownloadQueueItem(Terminal, FileList, TargetDirectory,
-      &CopyParam, Params, SingleFile, CopyParam.QueueParallel);
+    QueueItem =
+      new TDownloadQueueItem(
+        Terminal, FileList, TargetDirectory, &CopyParam, Params, CopyParam.QueueParallel);
   }
   AddQueueItem(Queue, QueueItem, Terminal);
 }
@@ -3942,7 +3929,7 @@ public:
   __fastcall TEditorUploadQueueItem(
       TTerminal * Terminal, TStrings * FilesToCopy, const UnicodeString & TargetDir,
       const TCopyParamType * CopyParam, int Params) :
-    TUploadQueueItem(Terminal, FilesToCopy, TargetDir, CopyParam, Params, true, false)
+    TUploadQueueItem(Terminal, FilesToCopy, TargetDir, CopyParam, Params, false)
   {
   }
 
@@ -6411,6 +6398,24 @@ void __fastcall TCustomScpExplorerForm::DoFullSynchronize(
   FullSynchronize(Params, OnProcessedItem, OnUpdatedSynchronizationChecklistItems);
 }
 //---------------------------------------------------------------------------
+void TCustomScpExplorerForm::DoQueueSynchronize(void * Token)
+{
+  TSynchronizeParams & Params = *static_cast<TSynchronizeParams *>(Token);
+
+  int CopyParams = TTerminal::GetSynchronizeCopyParams(Params.Params);
+  TCopyParamType SyncCopyParam = TTerminal::GetSynchronizeCopyParam(Params.CopyParam, Params.Params);
+
+  bool Parallel = GUIConfiguration->CurrentCopyParam.QueueParallel;
+
+  int Index = 0;
+  const TSynchronizeChecklist::TItem * ChecklistItem;
+  while (Params.Checklist->GetNextChecked(Index, ChecklistItem))
+  {
+    TQueueItem * QueueItem = Terminal->SynchronizeToQueue(ChecklistItem, &SyncCopyParam, CopyParams, Parallel);
+    AddQueueItem(Queue, QueueItem, Terminal);
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::DoSynchronizeChecklistCalculateSize(
   TSynchronizeChecklist * Checklist, const TSynchronizeChecklist::TItemList & Items, void * Token)
 {
@@ -6638,9 +6643,15 @@ int __fastcall TCustomScpExplorerForm::DoFullSynchronizeDirectories(
       {
         if (FLAGSET(Params, TTerminal::spPreviewChanges))
         {
+          TQueueSynchronizeEvent OnQueueSynchronize = NULL;
+          if (Visible && FLAGCLEAR(Params, TTerminal::spTimestamp))
+          {
+            OnQueueSynchronize = DoQueueSynchronize;
+          }
           if (!DoSynchronizeChecklistDialog(
                 Checklist, Mode, Params, LocalDirectory, RemoteDirectory, CustomCommandMenu, DoFullSynchronize,
-                DoSynchronizeChecklistCalculateSize, DoSynchronizeMove, DoSynchronizeBrowse, &SynchronizeParams))
+                OnQueueSynchronize, DoSynchronizeChecklistCalculateSize, DoSynchronizeMove, DoSynchronizeBrowse,
+                &SynchronizeParams))
           {
             Result = -1;
           }

+ 1 - 0
source/forms/CustomScpExplorer.h

@@ -713,6 +713,7 @@ protected:
   void __fastcall DoFullSynchronize(
     void * Token, TProcessedSynchronizationChecklistItem OnProcessedItem,
     TUpdatedSynchronizationChecklistItems OnUpdatedSynchronizationChecklistItems);
+  void DoQueueSynchronize(void * Token);
   void __fastcall DoSynchronizeChecklistCalculateSize(
     TSynchronizeChecklist * Checklist, const TSynchronizeChecklist::TItemList & Items, void * Token);
   void __fastcall DoSynchronizeMove(

+ 50 - 3
source/forms/SynchronizeChecklist.cpp

@@ -33,13 +33,14 @@ bool __fastcall DoSynchronizeChecklistDialog(TSynchronizeChecklist * Checklist,
   TSynchronizeMode Mode, int Params,
   const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
   TCustomCommandMenuEvent OnCustomCommandMenu, TFullSynchronizeEvent OnSynchronize,
+  TQueueSynchronizeEvent OnQueueSynchronize,
   TSynchronizeChecklistCalculateSize OnSynchronizeChecklistCalculateSize, TSynchronizeMoveEvent OnSynchronizeMove,
   TSynchronizeBrowseEvent OnSynchronizeBrowse, void * Token)
 {
   std::unique_ptr<TSynchronizeChecklistDialog> Dialog(
     new TSynchronizeChecklistDialog(
       Application, Mode, Params, LocalDirectory, RemoteDirectory, OnCustomCommandMenu, OnSynchronize,
-      OnSynchronizeChecklistCalculateSize, OnSynchronizeMove, OnSynchronizeBrowse, Token));
+      OnQueueSynchronize, OnSynchronizeChecklistCalculateSize, OnSynchronizeMove, OnSynchronizeBrowse, Token));
   return Dialog->Execute(Checklist);
 }
 //---------------------------------------------------------------------
@@ -47,6 +48,7 @@ __fastcall TSynchronizeChecklistDialog::TSynchronizeChecklistDialog(
   TComponent * AOwner, TSynchronizeMode Mode, int Params,
   const UnicodeString & LocalDirectory, const UnicodeString & RemoteDirectory,
   TCustomCommandMenuEvent OnCustomCommandMenu, TFullSynchronizeEvent OnSynchronize,
+  TQueueSynchronizeEvent OnQueueSynchronize,
   TSynchronizeChecklistCalculateSize OnSynchronizeChecklistCalculateSize, TSynchronizeMoveEvent OnSynchronizeMove,
   TSynchronizeBrowseEvent OnSynchronizeBrowse, void * Token)
   : TForm(AOwner)
@@ -62,6 +64,7 @@ __fastcall TSynchronizeChecklistDialog::TSynchronizeChecklistDialog(
   FOnSynchronizeBrowse = OnSynchronizeBrowse;
   DebugAssert(OnSynchronize != NULL);
   FOnSynchronize = OnSynchronize;
+  FOnQueueSynchronize = OnQueueSynchronize;
   FToken = Token;
   UseSystemSettings(this);
   UseDesktopFont(ListView);
@@ -88,6 +91,12 @@ __fastcall TSynchronizeChecklistDialog::TSynchronizeChecklistDialog(
   CustomCommandsButton2->Visible = CustomCommandsAction->Visible;
   MenuButton(CustomCommandsButton2);
   MenuButton(ToolsMenuButton);
+
+  // Other dialogs use !IsMainFormLike
+  if (FOnQueueSynchronize != NULL)
+  {
+    OkButton->Style = TCustomButton::bsSplitButton;
+  }
 }
 //---------------------------------------------------------------------
 __fastcall TSynchronizeChecklistDialog::~TSynchronizeChecklistDialog()
@@ -1306,7 +1315,7 @@ void __fastcall TSynchronizeChecklistDialog::UpdatedSynchronizationChecklistItem
   UpdateControls();
 }
 //---------------------------------------------------------------------------
-void __fastcall TSynchronizeChecklistDialog::OkButtonClick(TObject * /*Sender*/)
+void TSynchronizeChecklistDialog::DoSynchronize(bool Queue)
 {
   ListView->SelectAll(smNone);
   for (int Index = 0; Index < ListView->Items->Count; Index++)
@@ -1320,12 +1329,35 @@ void __fastcall TSynchronizeChecklistDialog::OkButtonClick(TObject * /*Sender*/)
   UpdateControls();
   try
   {
-    FOnSynchronize(FToken, ProcessedItem, UpdatedSynchronizationChecklistItems);
+    if (Queue)
+    {
+      FOnQueueSynchronize(FToken);
+    }
+    else
+    {
+      FOnSynchronize(FToken, ProcessedItem, UpdatedSynchronizationChecklistItems);
+    }
   }
   catch (Exception & E)
   {
     FException.reset(CloneException(&E));
   }
+
+  // Needed when called from the drop down menu
+  ModalResult = OkButton->ModalResult;
+}
+//---------------------------------------------------------------------------
+void __fastcall TSynchronizeChecklistDialog::OkButtonClick(TObject *)
+{
+  bool Queue = OpenInNewWindow();
+  if (Queue && (FOnQueueSynchronize == NULL))
+  {
+    Beep();
+  }
+  else
+  {
+    DoSynchronize(Queue);
+  }
 }
 //---------------------------------------------------------------------------
 void TSynchronizeChecklistDialog::CalculateSize(bool All)
@@ -1788,3 +1820,18 @@ void __fastcall TSynchronizeChecklistDialog::FindMoveCandidateActionExecute(TObj
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TSynchronizeChecklistDialog::StartItemClick(TObject *)
+{
+  DoSynchronize(false);
+}
+//---------------------------------------------------------------------------
+void __fastcall TSynchronizeChecklistDialog::OkButtonDropDownClick(TObject *)
+{
+  MenuPopup(OkPopupMenu, OkButton);
+}
+//---------------------------------------------------------------------------
+void __fastcall TSynchronizeChecklistDialog::StartQueueItemClick(TObject *)
+{
+  DoSynchronize(true);
+}
+//---------------------------------------------------------------------------

+ 14 - 0
source/forms/SynchronizeChecklist.dfm

@@ -1386,6 +1386,7 @@ object SynchronizeChecklistDialog: TSynchronizeChecklistDialog
       ModalResult = 1
       TabOrder = 0
       OnClick = OkButtonClick
+      OnDropDownClick = OkButtonDropDownClick
     end
     object CancelButton: TButton
       Left = 8
@@ -3699,4 +3700,17 @@ object SynchronizeChecklistDialog: TSynchronizeChecklistDialog
       Action = FindMoveCandidateAction
     end
   end
+  object OkPopupMenu: TPopupMenu
+    Left = 224
+    Top = 336
+    object StartItem: TMenuItem
+      Caption = '&Start'
+      Default = True
+      OnClick = StartItemClick
+    end
+    object StartQueueItem: TMenuItem
+      Caption = 'Start on &Background'
+      OnClick = StartQueueItemClick
+    end
+  end
 end

+ 9 - 0
source/forms/SynchronizeChecklist.h

@@ -77,6 +77,9 @@ __published:
   TMenuItem *CalculateAll2;
   TMenuItem *N4;
   TMenuItem *FindMoveCandidate1;
+  TPopupMenu *OkPopupMenu;
+  TMenuItem *StartItem;
+  TMenuItem *StartQueueItem;
   void __fastcall HelpButtonClick(TObject * Sender);
   void __fastcall FormShow(TObject * Sender);
   void __fastcall StatusBarDrawPanel(TStatusBar *StatusBar,
@@ -119,12 +122,16 @@ __published:
   void __fastcall ToolsMenuButtonClick(TObject *Sender);
   void __fastcall FindMoveCandidateActionExecute(TObject *Sender);
   void __fastcall FormAfterMonitorDpiChanged(TObject *Sender, int OldDPI, int NewDPI);
+  void __fastcall StartItemClick(TObject *Sender);
+  void __fastcall OkButtonDropDownClick(TObject *Sender);
+  void __fastcall StartQueueItemClick(TObject *Sender);
 
 public:
   __fastcall TSynchronizeChecklistDialog(
     TComponent * AOwner, TSynchronizeMode Mode, int Params,
     const UnicodeString & LocalDirectory, const UnicodeString & RemoteDirectory,
     TCustomCommandMenuEvent OnCustomCommandMenu, TFullSynchronizeEvent OnSynchronize,
+    TQueueSynchronizeEvent OnQueueSynchronize,
     TSynchronizeChecklistCalculateSize OnSynchronizeChecklistCalculateSize, TSynchronizeMoveEvent OnSynchronizeMove,
     TSynchronizeBrowseEvent OnSynchronizeBrowse, void * Token);
   virtual __fastcall ~TSynchronizeChecklistDialog();
@@ -154,6 +161,7 @@ protected:
   typedef std::map<const TSynchronizeChecklist::TItem *, TSynchronizeChecklist::TAction> TActions;
   TActions FActions;
   TFullSynchronizeEvent FOnSynchronize;
+  TQueueSynchronizeEvent FOnQueueSynchronize;
   void * FToken;
   bool FSynchronizing;
   std::unique_ptr<Exception> FException;
@@ -203,6 +211,7 @@ protected:
   TIEListViewColProperties * GetColProperties();
   bool IterateItems(TListItem *& Item, TItemStates States);
   bool IterateSelectedItems(TListItem *& Item);
+  void DoSynchronize(bool Queue);
 };
 //----------------------------------------------------------------------------
 #endif

+ 2 - 0
source/windows/WinInterface.h

@@ -392,6 +392,7 @@ typedef void __fastcall (__closure *TCustomCommandMenuEvent)
 typedef void __fastcall (__closure *TFullSynchronizeEvent)(
   void * Token, TProcessedSynchronizationChecklistItem OnProcessedItem,
   TUpdatedSynchronizationChecklistItems OnUpdatedSynchronizationChecklistItems);
+typedef void (__closure *TQueueSynchronizeEvent)(void * Token);
 typedef void __fastcall (__closure *TSynchronizeChecklistCalculateSize)
   (TSynchronizeChecklist * Checklist, const TSynchronizeChecklist::TItemList & Items, void * Token);
 typedef void __fastcall (__closure *TSynchronizeMoveEvent)(
@@ -402,6 +403,7 @@ bool __fastcall DoSynchronizeChecklistDialog(TSynchronizeChecklist * Checklist,
   TSynchronizeMode Mode, int Params,
   const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
   TCustomCommandMenuEvent OnCustomCommandMenu, TFullSynchronizeEvent OnSynchronize,
+  TQueueSynchronizeEvent OnQueueSynchronize,
   TSynchronizeChecklistCalculateSize OnSynchronizeChecklistCalculateSize, TSynchronizeMoveEvent OnSynchronizeMove,
   TSynchronizeBrowseEvent OnSynchronizeBrowse, void * Token);