瀏覽代碼

Bug 1785: Allow seeing a complete list of files that are part of a background transfer

https://winscp.net/tracker/1785
(cherry picked from commit 24dcc6ebb2248b4903ed6a7df73b23d42e4123f5)

# Conflicts:
#	source/forms/CustomScpExplorer.cpp
#	source/forms/CustomScpExplorer.h

Source commit: 2a38e955d435d2c4b91d28190f56a80038d42147
Martin Prikryl 5 年之前
父節點
當前提交
22ca658c0e

+ 99 - 11
source/core/Queue.cpp

@@ -722,8 +722,7 @@ TTerminalQueueStatus * __fastcall TTerminalQueue::CreateStatus(TTerminalQueueSta
   return Status;
 }
 //---------------------------------------------------------------------------
-bool __fastcall TTerminalQueue::ItemGetData(TQueueItem * Item,
-  TQueueItemProxy * Proxy)
+bool __fastcall TTerminalQueue::ItemGetData(TQueueItem * Item, TQueueItemProxy * Proxy, TQueueFileList * FileList)
 {
   // to prevent deadlocks when closing queue from other thread
   bool Result = !FFinished;
@@ -734,7 +733,14 @@ bool __fastcall TTerminalQueue::ItemGetData(TQueueItem * Item,
     Result = (FDoneItems->IndexOf(Item) >= 0) || (FItems->IndexOf(Item) >= 0);
     if (Result)
     {
-      Item->GetData(Proxy);
+      if (FileList != NULL)
+      {
+        Result = Item->UpdateFileList(FileList);
+      }
+      else
+      {
+        Item->GetData(Proxy);
+      }
     }
   }
 
@@ -1713,6 +1719,12 @@ void __fastcall TQueueItem::GetData(TQueueItemProxy * Proxy)
   }
 }
 //---------------------------------------------------------------------------
+bool __fastcall TQueueItem::UpdateFileList(TQueueFileList *)
+{
+  // noop - implemented in TTransferQueueItem
+  return false;
+}
+//---------------------------------------------------------------------------
 void __fastcall TQueueItem::Execute(TTerminalItem * TerminalItem)
 {
   {
@@ -1796,7 +1808,7 @@ bool __fastcall TQueueItemProxy::Update()
 
   TQueueItem::TStatus PrevStatus = Status;
 
-  bool Result = FQueue->ItemGetData(FQueueItem, this);
+  bool Result = FQueue->ItemGetData(FQueueItem, this, NULL);
 
   if ((FQueueStatus != NULL) && (PrevStatus != Status))
   {
@@ -1806,6 +1818,11 @@ bool __fastcall TQueueItemProxy::Update()
   return Result;
 }
 //---------------------------------------------------------------------------
+bool __fastcall TQueueItemProxy::UpdateFileList(TQueueFileList * FileList)
+{
+  return FQueue->ItemGetData(FQueueItem, NULL, FileList);
+}
+//---------------------------------------------------------------------------
 bool __fastcall TQueueItemProxy::ExecuteNow()
 {
   return FQueue->ItemExecuteNow(FQueueItem);
@@ -2028,6 +2045,24 @@ TQueueItemProxy * __fastcall TTerminalQueueStatus::FindByQueueItem(
   return NULL;
 }
 //---------------------------------------------------------------------------
+bool __fastcall TTerminalQueueStatus::UpdateFileList(TQueueItemProxy * ItemProxy, TQueueFileList * FileList)
+{
+  bool Result;
+  if (ItemProxy != NULL)
+  {
+    Result = ItemProxy->UpdateFileList(FileList);
+  }
+  else
+  {
+    Result = (FileList->GetCount() > 0);
+    if (Result)
+    {
+      FileList->Clear();
+    }
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 // TBootstrapQueueItem
 //---------------------------------------------------------------------------
 __fastcall TBootstrapQueueItem::TBootstrapQueueItem()
@@ -2131,15 +2166,14 @@ void __fastcall TTransferQueueItem::DoExecute(TTerminal * Terminal)
   TLocatedQueueItem::DoExecute(Terminal);
 
   DebugAssert(Terminal != NULL);
-  TParallelOperation ParallelOperation(FInfo->Side);
-  FParallelOperation = &ParallelOperation;
+  FParallelOperation.reset(new TParallelOperation(FInfo->Side));
   try
   {
-    DoTransferExecute(Terminal, FParallelOperation);
+    DoTransferExecute(Terminal, FParallelOperation.get());
   }
   __finally
   {
-    FParallelOperation = NULL;
+    FParallelOperation->WaitFor();
   }
 }
 //---------------------------------------------------------------------------
@@ -2155,7 +2189,7 @@ void __fastcall TTransferQueueItem::ProgressUpdated()
 
     {
       TGuard Guard(FSection);
-      DebugAssert(FParallelOperation != NULL);
+      DebugAssert(FParallelOperation.get() != NULL);
       // Won't be initialized, if the operation is not eligible for parallel transfers (like cpDelete).
       // We can probably move the check outside of the guard.
       if (FParallelOperation->IsInitialized())
@@ -2192,10 +2226,25 @@ void __fastcall TTransferQueueItem::ProgressUpdated()
 //---------------------------------------------------------------------------
 TQueueItem * __fastcall TTransferQueueItem::CreateParallelOperation()
 {
-  DebugAssert(FParallelOperation != NULL);
+  DebugAssert(FParallelOperation.get() != NULL);
 
   FParallelOperation->AddClient();
-  return new TParallelTransferQueueItem(this, FParallelOperation);
+  return new TParallelTransferQueueItem(this, FParallelOperation.get());
+}
+//---------------------------------------------------------------------------
+bool __fastcall TTransferQueueItem::UpdateFileList(TQueueFileList * FileList)
+{
+  TGuard Guard(FSection);
+  bool Result;
+  if ((FParallelOperation.get() != NULL) && FParallelOperation->IsInitialized())
+  {
+    Result = FParallelOperation->UpdateFileList(FileList);
+  }
+  else
+  {
+    Result = false;
+  }
+  return Result;
 }
 //---------------------------------------------------------------------------
 // TUploadQueueItem
@@ -2894,3 +2943,42 @@ bool __fastcall TTerminalThread::Finished()
 {
   return TSimpleThread::Finished() || FAbandoned;
 }
+//---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
+TQueueFileList::TQueueFileList() :
+  FList(new TStringList())
+{
+  Clear();
+}
+//---------------------------------------------------------------------------
+void TQueueFileList::Clear()
+{
+  FList->Clear();
+  FLastParallelOperation = NULL;
+  FLastParallelOperationVersion = -1;
+}
+//---------------------------------------------------------------------------
+void TQueueFileList::Add(const UnicodeString & FileName, int State)
+{
+  FList->AddObject(FileName, reinterpret_cast<TObject *>(State));
+}
+//---------------------------------------------------------------------------
+UnicodeString TQueueFileList::GetFileName(int Index) const
+{
+  return FList->Strings[Index];
+}
+//---------------------------------------------------------------------------
+int TQueueFileList::GetState(int Index) const
+{
+  return reinterpret_cast<int>(FList->Objects[Index]);
+}
+//---------------------------------------------------------------------------
+void TQueueFileList::SetState(int Index, int State)
+{
+  FList->Objects[Index] = reinterpret_cast<TObject *>(State);
+}
+//---------------------------------------------------------------------------
+int TQueueFileList::GetCount() const
+{
+  return FList->Count;
+}

+ 27 - 2
source/core/Queue.h

@@ -53,6 +53,7 @@ class TQueueItem;
 class TTerminalQueue;
 class TQueueItemProxy;
 class TTerminalQueueStatus;
+class TQueueFileList;
 //---------------------------------------------------------------------------
 typedef void __fastcall (__closure * TQueueListUpdate)
   (TTerminalQueue * Queue);
@@ -124,7 +125,7 @@ protected:
   void __fastcall FreeItemsList(TList * List);
   void __fastcall UpdateStatusForList(
     TTerminalQueueStatus * Status, TList * List, TTerminalQueueStatus * Current);
-  bool __fastcall ItemGetData(TQueueItem * Item, TQueueItemProxy * Proxy);
+  bool __fastcall ItemGetData(TQueueItem * Item, TQueueItemProxy * Proxy, TQueueFileList * FileList);
   bool __fastcall ItemProcessUserAction(TQueueItem * Item, void * Arg);
   bool __fastcall ItemMove(TQueueItem * Item, TQueueItem * BeforeItem);
   bool __fastcall ItemExecuteNow(TQueueItem * Item);
@@ -203,6 +204,7 @@ protected:
   virtual void __fastcall DoExecute(TTerminal * Terminal) = 0;
   void __fastcall SetProgress(TFileOperationProgressType & ProgressData);
   void __fastcall GetData(TQueueItemProxy * Proxy);
+  virtual bool __fastcall UpdateFileList(TQueueFileList * FileList);
   void __fastcall SetCPSLimit(unsigned long CPSLimit);
   unsigned long __fastcall GetCPSLimit();
   virtual unsigned long __fastcall DefaultCPSLimit();
@@ -220,6 +222,7 @@ friend class TTerminalQueue;
 
 public:
   bool __fastcall Update();
+  bool __fastcall UpdateFileList(TQueueFileList * FileList);
   bool __fastcall ProcessUserAction();
   bool __fastcall Move(bool Sooner);
   bool __fastcall Move(TQueueItemProxy * BeforeItem);
@@ -275,6 +278,8 @@ public:
 
   bool __fastcall IsOnlyOneActiveAndNoPending();
 
+  bool __fastcall UpdateFileList(TQueueItemProxy * ItemProxy, TQueueFileList * FileList);
+
 protected:
   __fastcall TTerminalQueueStatus();
 
@@ -339,13 +344,14 @@ protected:
   int FParams;
   bool FParallel;
   DWORD FLastParallelOperationAdded;
-  TParallelOperation * FParallelOperation;
+  std::unique_ptr<TParallelOperation> FParallelOperation;
 
   virtual unsigned long __fastcall DefaultCPSLimit();
   virtual void __fastcall DoExecute(TTerminal * Terminal);
   virtual void __fastcall DoTransferExecute(TTerminal * Terminal, TParallelOperation * ParallelOperation) = 0;
   virtual void __fastcall ProgressUpdated();
   virtual TQueueItem * __fastcall CreateParallelOperation();
+  virtual bool __fastcall UpdateFileList(TQueueFileList * FileList);
 };
 //---------------------------------------------------------------------------
 class TUploadQueueItem : public TTransferQueueItem
@@ -456,4 +462,23 @@ private:
   void __fastcall TerminalInitializeLog(TObject * Sender);
 };
 //---------------------------------------------------------------------------
+enum TQueueFileState { qfsQueued = 0, qfsProcessed = 1 };
+//---------------------------------------------------------------------------
+class TQueueFileList
+{
+friend class TParallelOperation;
+public:
+  TQueueFileList();
+  void Clear();
+  void Add(const UnicodeString & FileName, int State);
+  UnicodeString GetFileName(int Index) const;
+  int GetState(int Index) const;
+  void SetState(int Index, int State);
+  int GetCount() const;
+private:
+  std::unique_ptr<TStrings> FList;
+  TParallelOperation * FLastParallelOperation;
+  int FLastParallelOperationVersion;
+};
+//---------------------------------------------------------------------------
 #endif

+ 72 - 7
source/core/Terminal.cpp

@@ -615,6 +615,7 @@ int TCollectedFileList::Add(const UnicodeString & FileName, TObject * Object, bo
   Data.Object = Object;
   Data.Dir = Dir;
   Data.Recursed = true;
+  Data.State = 0;
   FList.push_back(Data);
   return Count() - 1;
 }
@@ -654,6 +655,16 @@ bool TCollectedFileList::IsRecursed(int Index) const
   return FList[Index].Recursed;
 }
 //---------------------------------------------------------------------------
+int TCollectedFileList::GetState(int Index) const
+{
+  return FList[Index].State;
+}
+//---------------------------------------------------------------------------
+void TCollectedFileList::SetState(int Index, int State)
+{
+  FList[Index].State = State;
+}
+//---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 TParallelOperation::TParallelOperation(TOperationSide Side)
 {
@@ -664,6 +675,7 @@ TParallelOperation::TParallelOperation(TOperationSide Side)
   FMainOperationProgress = NULL;
   DebugAssert((Side == osLocal) || (Side == osRemote));
   FSide = Side;
+  FVersion = 0;
 }
 //---------------------------------------------------------------------------
 void TParallelOperation::Init(
@@ -681,6 +693,7 @@ void TParallelOperation::Init(
   FParams = Params;
   FMainOperationProgress = MainOperationProgress;
   FMainName = MainName;
+  FListIndex = 0;
   FIndex = 0;
 }
 //---------------------------------------------------------------------------
@@ -773,7 +786,7 @@ void TParallelOperation::Done(const UnicodeString & FileName, bool Dir, bool Suc
         // This is actually not useful at the moment, as when creating directory fails and "Skip" is pressed,
         // the current code in CopyToRemote/CreateDirectory will behave as, if it succedded, so Successs will be true here.
         FDirectories.erase(DirectoryIterator);
-        if (FFileList->Count > 0)
+        if (FFileList->Count > FListIndex)
         {
           UnicodeString FileNameWithSlash;
           if (FSide == osLocal)
@@ -787,7 +800,7 @@ void TParallelOperation::Done(const UnicodeString & FileName, bool Dir, bool Suc
 
           // It can actually be a different list than the one the directory was taken from,
           // but that does not matter that much. It should not happen anyway, as more lists should be in scripting only.
-          TCollectedFileList * Files = DebugNotNull(dynamic_cast<TCollectedFileList *>(FFileList->Objects[0]));
+          TCollectedFileList * Files = GetFileList(FListIndex);
           int Index = 0;
           while (Index < Files->Count())
           {
@@ -796,6 +809,8 @@ void TParallelOperation::Done(const UnicodeString & FileName, bool Dir, bool Suc
               // We should add the file to "skip" counters in the OperationProgress,
               // but an interactive foreground transfer is not doing that either yet.
               Files->Delete(Index);
+              // Force re-update
+              FVersion++;
               if (Index < FIndex)
               {
                 FIndex--;
@@ -817,12 +832,17 @@ bool TParallelOperation::CheckEnd(TCollectedFileList * Files)
   bool Result = (FIndex >= Files->Count());
   if (Result)
   {
-    FFileList->Delete(0);
+    FListIndex++;
     FIndex = 0;
   }
   return Result;
 }
 //---------------------------------------------------------------------------
+TCollectedFileList * TParallelOperation::GetFileList(int Index)
+{
+  return DebugNotNull(dynamic_cast<TCollectedFileList *>(FFileList->Objects[Index]));
+}
+//---------------------------------------------------------------------------
 int TParallelOperation::GetNext(TTerminal * Terminal, UnicodeString & FileName, TObject *& Object, UnicodeString & TargetDir, bool & Dir, bool & Recursed)
 {
   TGuard Guard(FSection.get());
@@ -830,9 +850,9 @@ int TParallelOperation::GetNext(TTerminal * Terminal, UnicodeString & FileName,
   TCollectedFileList * Files;
   do
   {
-    if (FFileList->Count > 0)
+    if (FFileList->Count > FListIndex)
     {
-      Files = DebugNotNull(dynamic_cast<TCollectedFileList *>(FFileList->Objects[0]));
+      Files = GetFileList(FListIndex);
       // can happen if the file was excluded by file mask
       if (CheckEnd(Files))
       {
@@ -849,7 +869,7 @@ int TParallelOperation::GetNext(TTerminal * Terminal, UnicodeString & FileName,
 
   if (Files != NULL)
   {
-    UnicodeString RootPath = FFileList->Strings[0];
+    UnicodeString RootPath = FFileList->Strings[FListIndex];
 
     FileName = Files->GetFileName(FIndex);
     Object = Files->GetObject(FIndex);
@@ -920,12 +940,57 @@ int TParallelOperation::GetNext(TTerminal * Terminal, UnicodeString & FileName,
         FDirectories.insert(std::make_pair(FileName, DirectoryData));
       }
 
+      // The current implementation of UpdateFileList relies on this specific way of changing state
+      // (all files before FIndex one-by-one)
+      Files->SetState(FIndex, qfsProcessed);
       FIndex++;
       CheckEnd(Files);
     }
   }
 
-  FProbablyEmpty = (FFileList->Count == 0);
+  FProbablyEmpty = (FFileList->Count == FListIndex);
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+bool TParallelOperation::UpdateFileList(TQueueFileList * UpdateFileList)
+{
+  TGuard Guard(FSection.get());
+
+  bool Result =
+    (UpdateFileList->FLastParallelOperation != this) ||
+    (UpdateFileList->FLastParallelOperationVersion != FVersion);
+
+  DebugAssert(FFileList->Count == 1);
+  TCollectedFileList * Files = GetFileList(0);
+
+  if (!Result && (UpdateFileList->GetCount() != Files->Count()))
+  {
+    DebugAssert(false);
+    Result = true;
+  }
+
+  if (Result)
+  {
+    UpdateFileList->Clear();
+    for (int Index = 0; Index < Files->Count(); Index++)
+    {
+      UpdateFileList->Add(Files->GetFileName(Index), Files->GetState(Index));
+    }
+  }
+  else
+  {
+    int Index = ((FListIndex == 0) ? FIndex : Files->Count()) - 1;
+    while ((Index >= 0) && (UpdateFileList->GetState(Index) != Files->GetState(Index)))
+    {
+      UpdateFileList->SetState(Index, Files->GetState(Index));
+      Index--;
+      Result = true;
+    }
+  }
+
+  UpdateFileList->FLastParallelOperation = this;
+  UpdateFileList->FLastParallelOperationVersion = FVersion;
 
   return Result;
 }

+ 9 - 0
source/core/Terminal.h

@@ -815,6 +815,8 @@ public:
   TObject * GetObject(int Index) const;
   bool IsDir(int Index) const;
   bool IsRecursed(int Index) const;
+  int GetState(int Index) const;
+  void SetState(int Index, int State);
 
 private:
   struct TFileData
@@ -823,11 +825,14 @@ private:
     TObject * Object;
     bool Dir;
     bool Recursed;
+    int State;
   };
   typedef std::vector<TFileData> TFileDataList;
   TFileDataList FList;
 };
 //---------------------------------------------------------------------------
+class TQueueFileList;
+//---------------------------------------------------------------------------
 class TParallelOperation
 {
 public:
@@ -847,6 +852,7 @@ public:
     TTerminal * Terminal, UnicodeString & FileName, TObject *& Object, UnicodeString & TargetDir,
     bool & Dir, bool & Recursed);
   void Done(const UnicodeString & FileName, bool Dir, bool Success);
+  bool UpdateFileList(TQueueFileList * UpdateFileList);
 
   __property TOperationSide Side = { read = FSide };
   __property const TCopyParamType * CopyParam = { read = FCopyParam };
@@ -863,6 +869,7 @@ private:
   };
 
   std::unique_ptr<TStrings> FFileList;
+  int FListIndex;
   int FIndex;
   typedef std::map<UnicodeString, TDirectoryData> TDirectories;
   TDirectories FDirectories;
@@ -875,8 +882,10 @@ private:
   TFileOperationProgressType * FMainOperationProgress;
   TOperationSide FSide;
   UnicodeString FMainName;
+  int FVersion;
 
   bool CheckEnd(TCollectedFileList * Files);
+  TCollectedFileList * GetFileList(int Index);
 };
 //---------------------------------------------------------------------------
 struct TLocalFileHandle

+ 172 - 7
source/forms/CustomScpExplorer.cpp

@@ -212,6 +212,7 @@ __fastcall TCustomScpExplorerForm::TCustomScpExplorerForm(TComponent* Owner):
   FDoNotIdleCurrentTerminal = 0;
   FIncrementalSearching = 0;
   FIncrementalSearchHaveNext = false;
+  FQueueFileList.reset(new TQueueFileList());
 
   FEditorManager = new TEditorManager();
   FEditorManager->OnFileChange = ExecutedFileChanged;
@@ -225,6 +226,7 @@ __fastcall TCustomScpExplorerForm::TCustomScpExplorerForm(TComponent* Owner):
   FQueueStatusSection = new TCriticalSection();
   FQueueStatusInvalidated = false;
   FQueueItemInvalidated = false;
+  FQueueStatusUpdating = false;
   FQueueActedItem = NULL;
   FQueueController = new TQueueController(QueueView3);
 
@@ -260,11 +262,14 @@ __fastcall TCustomScpExplorerForm::TCustomScpExplorerForm(TComponent* Owner):
   UseDesktopFont(RemoteDirView);
   UseDesktopFont(RemoteDriveView);
   UseDesktopFont(QueueView3);
+  UseDesktopFont(QueueFileList);
   UseDesktopFont(QueueLabel);
   UseDesktopFont(RemoteStatusBar);
 
   reinterpret_cast<TLabel*>(QueueSplitter)->OnDblClick = QueueSplitterDblClick;
   QueueSplitter->ShowHint = true;
+  reinterpret_cast<TLabel*>(QueueFileListSplitter)->OnDblClick = QueueFileListSplitterDblClick;
+  QueueFileListSplitter->ShowHint = true;
   RemotePanelSplitter->ShowHint = true;
 
   UpdateImages();
@@ -277,7 +282,6 @@ __fastcall TCustomScpExplorerForm::TCustomScpExplorerForm(TComponent* Owner):
   FSessionColors->ColorDepth = cd32Bit;
   AddFixedSessionImages();
   SessionsPageControl->Images = FSessionColors;
-  QueueLabel->FocusControl = QueueView3;
   UpdateQueueLabel();
   FRemoteDirViewWasFocused = true;
 
@@ -687,7 +691,11 @@ void __fastcall TCustomScpExplorerForm::UpdateQueueStatus(bool QueueChanging)
     Configuration->Usage->SetMax(L"MaxQueueLength", FMaxQueueLength);
   }
 
-  FQueueController->UpdateQueueStatus(FQueueStatus);
+  {
+    TAutoFlag Flag(FQueueStatusUpdating);
+    FQueueController->UpdateQueueStatus(FQueueStatus);
+  }
+  UpdateQueueFileList();
   SetQueueProgress();
 
   UpdateQueueView();
@@ -746,6 +754,7 @@ void __fastcall TCustomScpExplorerForm::QueueChanged()
 {
   if (FQueueStatus != NULL)
   {
+    // UpdateFileList implementation relies on the status being recreated when session changes
     delete FQueueStatus;
     FQueueStatus = NULL;
   }
@@ -844,6 +853,7 @@ void __fastcall TCustomScpExplorerForm::RefreshQueueItems()
     {
       NonVisualDataModule->UpdateNonVisibleActions();
       SetQueueProgress();
+      UpdateQueueFileList();
     }
   }
 }
@@ -1279,16 +1289,27 @@ void __fastcall TCustomScpExplorerForm::RestoreParams()
   LoadListViewStr(QueueView3, WinConfiguration->QueueView.Layout);
   QueueDock->Visible = WinConfiguration->QueueView.ToolBar;
   QueueLabel->Visible = WinConfiguration->QueueView.Label;
+  QueueFileList->Visible = WinConfiguration->QueueView.FileList;
+  QueueFileList->Height =
+    LoadDimension(WinConfiguration->QueueView.FileListHeight, WinConfiguration->QueueView.FileListHeightPixelsPerInch, this);
+  if (QueueFileList->Visible)
+  {
+    AdjustQueueLayout();
+  }
   UpdateCustomCommandsToolbar();
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::StoreParams()
 {
+  int APixelsPerInch = GetControlPixelsPerInch(this);
   WinConfiguration->QueueView.Height = QueuePanel->Height;
-  WinConfiguration->QueueView.HeightPixelsPerInch = GetControlPixelsPerInch(this);
+  WinConfiguration->QueueView.HeightPixelsPerInch = APixelsPerInch;
   WinConfiguration->QueueView.Layout = GetListViewStr(QueueView3);
   WinConfiguration->QueueView.ToolBar = QueueDock->Visible;
   WinConfiguration->QueueView.Label = QueueLabel->Visible;
+  WinConfiguration->QueueView.FileList = QueueFileList->Visible;
+  WinConfiguration->QueueView.FileListHeight = QueueFileList->Height;
+  WinConfiguration->QueueView.FileListHeightPixelsPerInch = APixelsPerInch;
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::CreateParams(TCreateParams & Params)
@@ -5068,6 +5089,26 @@ void __fastcall TCustomScpExplorerForm::ComponentShowing(Byte Component, bool va
     {
       UpdateCustomCommandsToolbar();
     }
+
+    if (Component == fcQueueFileList)
+    {
+      DebugAssert(!QueueFileListSplitter->Visible);
+      AdjustQueueLayout();
+      UpdateQueueFileList();
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::AdjustQueueLayout()
+{
+  int MinHeightLimit =
+    GetStaticQueuePanelComponentsHeight() +
+    QueueFileListSplitter->Height +
+    QueueFileList->Height +
+    GetMinQueueViewHeight();
+  if (QueuePanel->ClientHeight < MinHeightLimit)
+  {
+    QueuePanel->ClientHeight = MinHeightLimit;
   }
 }
 //---------------------------------------------------------------------------
@@ -5163,7 +5204,13 @@ void __fastcall TCustomScpExplorerForm::FixControlsPlacement()
     DirView(osOther)->ItemFocused->MakeVisible(false);
   }
   QueueSplitter->Visible = QueuePanel->Visible;
+  QueueFileListSplitter->Visible = QueueFileList->Visible;
   RemotePanelSplitter->Visible = RemoteDrivePanel->Visible;
+
+  TControl * QueueControlsOrder[] =
+    { QueueDock, QueueView3, QueueFileListSplitter, QueueFileList };
+  SetVerticalControlsOrder(QueueControlsOrder, LENOF(QueueControlsOrder));
+
 }
 //---------------------------------------------------------------------------
 TControl * __fastcall TCustomScpExplorerForm::GetComponent(Byte Component)
@@ -5175,6 +5222,7 @@ TControl * __fastcall TCustomScpExplorerForm::GetComponent(Byte Component)
     case fcQueueToolbar: return QueueDock;
     case fcRemoteTree: return RemoteDrivePanel;
     case fcSessionsTabs: return SessionsPageControl;
+    case fcQueueFileList: return QueueFileList;
     default: return NULL;
   }
 }
@@ -7899,6 +7947,19 @@ void __fastcall TCustomScpExplorerForm::QueueView3ContextPopup(
   return TopDock->Height + QueueSplitter->Height;
 }
 //---------------------------------------------------------------------------
+int __fastcall TCustomScpExplorerForm::GetStaticQueuePanelComponentsHeight()
+{
+  return
+    (QueueFileListSplitter->Visible ? QueueFileListSplitter->Height : 0) +
+    (QueueDock->Visible ? QueueDock->Height : 0) +
+    (QueueLabel->Visible ? QueueLabel->Height : 0);
+}
+//---------------------------------------------------------------------------
+int __fastcall TCustomScpExplorerForm::GetMinQueueViewHeight()
+{
+  return ScaleByTextHeight(this, 48);
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::QueueSplitterCanResize(
   TObject * /*Sender*/, int & NewSize, bool & Accept)
 {
@@ -7906,12 +7967,19 @@ void __fastcall TCustomScpExplorerForm::QueueSplitterCanResize(
   // resize the panel with strange value arrives, make sure it is ignored
   if (ComponentVisible[fcQueueView])
   {
-    int HeightLimit = ClientHeight - GetStaticComponentsHeight() -
-      RemotePanel->Constraints->MinHeight;
+    int MaxHeightLimit = ClientHeight - GetStaticComponentsHeight() - RemotePanel->Constraints->MinHeight;
+    if (NewSize > MaxHeightLimit)
+    {
+      NewSize = MaxHeightLimit;
+    }
 
-    if (NewSize > HeightLimit)
+    int MinHeightLimit =
+      GetStaticQueuePanelComponentsHeight() +
+      (QueueFileList->Visible ? QueueFileList->Height : 0) +
+      GetMinQueueViewHeight();
+    if (NewSize < MinHeightLimit)
     {
-      NewSize = HeightLimit;
+      NewSize = MinHeightLimit;
     }
   }
   else
@@ -7920,6 +7988,17 @@ void __fastcall TCustomScpExplorerForm::QueueSplitterCanResize(
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::QueueFileListSplitterCanResize(TObject *, int & NewSize, bool &)
+{
+  int TotalHeight = GetStaticQueuePanelComponentsHeight() + NewSize;
+  int QueueViewHeight = QueuePanel->ClientHeight - TotalHeight;
+
+  if (QueueViewHeight < GetMinQueueViewHeight())
+  {
+    NewSize -= (GetMinQueueViewHeight() - QueueViewHeight);
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::QueueView3StartDrag(TObject * /*Sender*/,
   TDragObject *& /*DragObject*/)
 {
@@ -8605,6 +8684,9 @@ void __fastcall TCustomScpExplorerForm::UpdateControls()
     QueueView3->Enabled = HasTerminal && Terminal->IsCapable[fcBackgroundTransfers];
     QueueView3->Color = QueueView3->Enabled ? GetWindowColor() : DisabledPanelColor();
     QueueView3->Font->Color =  GetWindowTextColor(QueueView3->Color);
+    QueueFileList->Enabled = QueueView3->Enabled;
+    QueueFileList->Color = QueueView3->Color;
+    QueueFileList->Font->Color = QueueView3->Font->Color;
     QueueLabelUpdateStatus();
 
     RemoteDirView->DarkMode = WinConfiguration->UseDarkTheme();
@@ -8936,6 +9018,11 @@ void __fastcall TCustomScpExplorerForm::QueueSplitterDblClick(TObject * /*Sender
   PostComponentHide(fcQueueView);
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::QueueFileListSplitterDblClick(TObject *)
+{
+  ComponentVisible[fcQueueFileList] = false;
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::ThemeChanged()
 {
   // We hoped this will refresh scrollbar colors, but it does not have any effect here.
@@ -10755,6 +10842,84 @@ void __fastcall TCustomScpExplorerForm::BrowseFile()
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::UpdateQueueFileList()
+{
+  if (!FQueueStatusUpdating)
+  {
+    bool Refresh;
+    if ((FQueueStatus != NULL) && QueueFileList->Visible)
+    {
+      Refresh = FQueueStatus->UpdateFileList(FQueueController->GetFocusedPrimaryItem(), FQueueFileList.get());
+    }
+    else
+    {
+      if (FQueueFileList->GetCount() > 0)
+      {
+        FQueueFileList->Clear();
+        Refresh = true;
+      }
+    }
+    int Count = FQueueFileList->GetCount();
+    if (QueueFileList->Items->Count != Count)
+    {
+      DebugAssert(Refresh);
+      QueueFileList->Items->Count = Count;
+    }
+    if (Refresh)
+    {
+      QueueFileList->Invalidate();
+    }
+    DebugAssert(QueueFileList->Items->Count == FQueueFileList->GetCount());
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::QueueFileListData(TObject *, TListItem * Item)
+{
+  Item->Caption = FQueueFileList->GetFileName(Item->Index);
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::QueueView3Change(TObject *, TListItem *, TItemChange)
+{
+  // should be improved, once we do not refresh the list when switching between details of the same batch
+  if (QueueFileList->Items->Count > 0)
+  {
+    QueueFileList->Items->Item[0]->MakeVisible(false);
+  }
+  UpdateQueueFileList();
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::QueueLabelGetStatus(TCustomPathLabel *, bool & Active)
+{
+  Active = QueueView3->Focused() || QueueFileList->Focused();
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::QueueFileListEnterExit(TObject *)
+{
+  QueueLabelUpdateStatus();
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::QueueFileListCustomDrawItem(
+  TCustomListView * Sender, TListItem * Item, TCustomDrawState, bool & DebugUsedArg(DefaultDraw))
+{
+  int State = FQueueFileList->GetState(Item->Index);
+  if (State == qfsProcessed)
+  {
+    Sender->Canvas->Font->Color = clGrayText;
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::QueueFileListColumnAutoSize()
+{
+  // We do not really need whole width, so we always preemptivelly keep free space for the vertical scrollbar
+  QueueFileList->Column[0]->Width =
+    QueueFileList->ClientWidth - GetSystemMetricsForControl(QueueFileList, SM_CXVSCROLL) - ScaleByTextHeight(QueueFileList, 8);
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::QueueFileListResize(TObject *)
+{
+  QueueFileListColumnAutoSize();
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::CloseApp()
 {
   // Called from TNonVisualDataModule::ExplorerActionsExecute, which sets busy flag.

+ 41 - 1
source/forms/CustomScpExplorer.dfm

@@ -189,14 +189,30 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
       Height = 19
       IndentVertical = 3
       AutoSizeVertical = True
+      OnGetStatus = QueueLabelGetStatus
       AutoSize = False
       Transparent = False
     end
+    object QueueFileListSplitter: TSplitter
+      Left = 0
+      Top = 117
+      Width = 620
+      Height = 3
+      Cursor = crSizeNS
+      Hint = 
+        'Drag to resize queue file list. Double click to hide queue file ' +
+        'list.'
+      Align = alBottom
+      AutoSnap = False
+      MinSize = 10
+      ResizeStyle = rsUpdate
+      OnCanResize = QueueFileListSplitterCanResize
+    end
     object QueueView3: TListView
       Left = 0
       Top = 45
       Width = 620
-      Height = 95
+      Height = 72
       Align = alClient
       Columns = <
         item
@@ -242,6 +258,7 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
       StateImages = GlyphsModule.QueueImages
       TabOrder = 0
       ViewStyle = vsReport
+      OnChange = QueueView3Change
       OnContextPopup = QueueView3ContextPopup
       OnDeletion = QueueView3Deletion
       OnEnter = QueueView3Enter
@@ -333,6 +350,29 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
         end
       end
     end
+    object QueueFileList: TListView
+      Left = 0
+      Top = 120
+      Width = 620
+      Height = 20
+      Align = alBottom
+      Columns = <
+        item
+        end>
+      DoubleBuffered = True
+      OwnerData = True
+      ReadOnly = True
+      ParentDoubleBuffered = False
+      ShowColumnHeaders = False
+      TabOrder = 2
+      TabStop = False
+      ViewStyle = vsReport
+      OnCustomDrawItem = QueueFileListCustomDrawItem
+      OnData = QueueFileListData
+      OnEnter = QueueFileListEnterExit
+      OnExit = QueueFileListEnterExit
+      OnResize = QueueFileListResize
+    end
   end
   object SessionsPageControl: TThemePageControl
     Left = 0

+ 17 - 0
source/forms/CustomScpExplorer.h

@@ -106,6 +106,8 @@ __published:
   TApplicationEvents *ApplicationEvents;
   TTBXToolbar *ReconnectToolbar;
   TTBXItem *TBXItem254;
+  TSplitter *QueueFileListSplitter;
+  TListView *QueueFileList;
   void __fastcall ApplicationMinimize(TObject * Sender);
   void __fastcall ApplicationRestore(TObject * Sender);
   void __fastcall RemoteDirViewContextPopup(TObject *Sender,
@@ -207,6 +209,13 @@ __published:
   void __fastcall DirViewChangeFocus(TObject *Sender, TListItem *Item);
   void __fastcall RemoteStatusBarMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y);
   void __fastcall RemoteDirViewResize(TObject *Sender);
+  void __fastcall QueueFileListSplitterCanResize(TObject *Sender, int &NewSize, bool &Accept);
+  void __fastcall QueueView3Change(TObject *Sender, TListItem *Item, TItemChange Change);
+  void __fastcall QueueLabelGetStatus(TCustomPathLabel *Sender, bool &Active);
+  void __fastcall QueueFileListEnterExit(TObject *Sender);
+  void __fastcall QueueFileListData(TObject *Sender, TListItem *Item);
+  void __fastcall QueueFileListCustomDrawItem(TCustomListView *Sender, TListItem *Item, TCustomDrawState State, bool &DefaultDraw);
+  void __fastcall QueueFileListResize(TObject *Sender);
 
 private:
   TManagedTerminal * FTerminal;
@@ -215,6 +224,7 @@ private:
   TCriticalSection * FQueueStatusSection;
   bool FQueueStatusInvalidated;
   bool FQueueItemInvalidated;
+  bool FQueueStatusUpdating;
   bool FFormRestored;
   bool FAutoOperation;
   TFileOperationFinishedEvent FOnFileOperationFinished;
@@ -281,6 +291,7 @@ private:
   TTerminal * FFileFindTerminal;
   UnicodeString FFileColorsCurrent;
   bool FInvalid;
+  std::auto_ptr<TQueueFileList> FQueueFileList;
   bool FStarted;
 
   bool __fastcall GetEnableFocusedOperation(TOperationSide Side, int FilesOnly);
@@ -300,6 +311,7 @@ private:
   void __fastcall AdHocCustomCommandValidate(const TCustomCommandType & Command);
   void __fastcall SetDockAllowDrag(bool value);
   void __fastcall QueueSplitterDblClick(TObject * Sender);
+  void __fastcall QueueFileListSplitterDblClick(TObject * Sender);
   void __fastcall AddQueueItem(TTerminalQueue * Queue, TTransferDirection Direction,
     TStrings * FileList, const UnicodeString TargetDirectory,
     const TGUICopyParamType & CopyParam, int Params);
@@ -332,6 +344,9 @@ private:
   void __fastcall LocalBookmarkClick(TObject * Sender);
   void __fastcall RemoteBookmarkClick(TObject * Sender);
   void __fastcall InitControls();
+  void __fastcall UpdateQueueFileList();
+  void __fastcall QueueFileListColumnAutoSize();
+  void __fastcall AdjustQueueLayout();
 
 protected:
   TOperationSide FCurrentSide;
@@ -691,6 +706,8 @@ protected:
   void __fastcall CenterReconnectToolbar();
   void __fastcall DoOpenFolderOrWorkspace(const UnicodeString & Name, bool ConnectFirstTerminal);
   virtual void __fastcall ThemeChanged();
+  int __fastcall GetStaticQueuePanelComponentsHeight();
+  int __fastcall GetMinQueueViewHeight();
   void __fastcall DetachTerminal(TObject * ATerminal);
 
 public:

+ 4 - 0
source/forms/NonVisual.cpp

@@ -483,6 +483,9 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
   UPDACT(QueueToolbarAction,
     ((TAction *)Action)->Enabled = ScpExplorer->ComponentVisible[fcQueueView];
     ((TAction *)Action)->Checked = ScpExplorer->ComponentVisible[fcQueueToolbar])
+  UPDACT(QueueFileListAction,
+    ((TAction *)Action)->Enabled = ScpExplorer->ComponentVisible[fcQueueView];
+    ((TAction *)Action)->Checked = ScpExplorer->ComponentVisible[fcQueueFileList])
   ;
 }
 //---------------------------------------------------------------------------
@@ -789,6 +792,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
     EXE(QueueSuspendOnceEmptyAction2, SetQueueOnceEmptyAction(QueueSuspendOnceEmptyAction2))
     EXE(QueueShutDownOnceEmptyAction2, SetQueueOnceEmptyAction(QueueShutDownOnceEmptyAction2))
     EXECOMP(QueueToolbar)
+    EXECOMP(QueueFileList)
     EXE(QueueItemSpeedAction, )
     ;
   }

+ 15 - 0
source/forms/NonVisual.dfm

@@ -1622,6 +1622,12 @@ object NonVisualDataModule: TNonVisualDataModule
       Caption = '&Toolbar'
       Hint = 'Hide/show queue list toolbar (on queue list panel)'
     end
+    object QueueFileListAction: TAction
+      Tag = 12
+      Category = 'Queue'
+      Caption = '&File List'
+      Hint = 'Hide/show full queue file list'
+    end
     object QueuePreferencesAction: TAction
       Tag = 12
       Category = 'Queue'
@@ -2361,6 +2367,9 @@ object NonVisualDataModule: TNonVisualDataModule
       object Toolbar5: TTBXItem
         Action = QueueToolbarAction
       end
+      object TBXItem85: TTBXItem
+        Action = QueueFileListAction
+      end
       object N70: TTBXSeparatorItem
       end
       object Customize5: TTBXItem
@@ -2504,6 +2513,9 @@ object NonVisualDataModule: TNonVisualDataModule
       object Toolbar4: TTBXItem
         Action = QueueToolbarAction
       end
+      object TBXItem84: TTBXItem
+        Action = QueueFileListAction
+      end
       object N68: TTBXSeparatorItem
       end
       object Customize4: TTBXItem
@@ -2849,6 +2861,9 @@ object NonVisualDataModule: TNonVisualDataModule
       object Toolbar3: TTBXItem
         Action = QueueToolbarAction
       end
+      object TBXItem83: TTBXItem
+        Action = QueueFileListAction
+      end
       object N65: TTBXSeparatorItem
       end
       object Customize3: TTBXItem

+ 5 - 0
source/forms/NonVisual.h

@@ -40,6 +40,7 @@
 #define fcSessionsTabs     0x25
 #define fcLocalPopup       0x26
 #define fcRemotePathComboBox 0x27
+#define fcQueueFileList    0x28
 
 #define fcExplorerMenuBand        0x31
 #define fcExplorerAddressBand     0x32
@@ -629,6 +630,10 @@ __published:    // IDE-managed Components
   TTBXItem *TBXItem81;
   TTBXSeparatorItem *TBXSeparatorItem12;
   TTBXItem *TBXItem82;
+  TAction *QueueFileListAction;
+  TTBXItem *TBXItem83;
+  TTBXItem *TBXItem84;
+  TTBXItem *TBXItem85;
   void __fastcall ExplorerActionsUpdate(TBasicAction *Action, bool &Handled);
   void __fastcall ExplorerActionsExecute(TBasicAction *Action, bool &Handled);
   void __fastcall SessionIdleTimerTimer(TObject *Sender);

+ 2 - 0
source/forms/ScpCommander.cpp

@@ -90,6 +90,8 @@ __fastcall TScpCommanderForm::TScpCommanderForm(TComponent* Owner)
   CopyPopup(QueueLabel, TopDock);
   CopyPopup(BottomDock, TopDock);
   CopyPopup(QueueSeparatorPanel, TopDock);
+  CopyPopup(QueueFileList, TopDock);
+  CopyPopup(QueueFileListSplitter, TopDock);
 
   LocalTopDock->PopupMenu = NonVisualDataModule->LocalPanelPopup;
   CopyPopup(LocalPathLabel, LocalTopDock);

+ 17 - 1
source/forms/ScpCommander.dfm

@@ -598,6 +598,9 @@ inherited ScpCommanderForm: TScpCommanderForm
           object TBXItem80: TTBXItem
             Action = NonVisualDataModule.QueueToolbarAction
           end
+          object TBXItem255: TTBXItem
+            Action = NonVisualDataModule.QueueFileListAction
+          end
           object TBXSeparatorItem22: TTBXSeparatorItem
           end
           object TBXSubmenuItem8: TTBXSubmenuItem
@@ -828,6 +831,11 @@ inherited ScpCommanderForm: TScpCommanderForm
           Action = NonVisualDataModule.QueueHideAction
           RadioItem = True
         end
+        object TBXSeparatorItem65: TTBXSeparatorItem
+        end
+        object TBXItem256: TTBXItem
+          Action = NonVisualDataModule.QueueFileListAction
+        end
       end
     end
     object SessionToolbar: TTBXToolbar
@@ -1340,14 +1348,22 @@ inherited ScpCommanderForm: TScpCommanderForm
     inherited QueueLabel: TPathLabel
       Width = 898
     end
+    inherited QueueFileListSplitter: TSplitter
+      Top = 93
+      Width = 898
+    end
     inherited QueueView3: TListView
       Width = 898
-      Height = 71
+      Height = 48
       TabStop = False
     end
     inherited QueueDock: TTBXDock
       Width = 898
     end
+    inherited QueueFileList: TListView
+      Top = 96
+      Width = 898
+    end
   end
   inherited SessionsPageControl: TThemePageControl
     Top = 156

+ 3 - 0
source/forms/ScpCommander.h

@@ -435,6 +435,9 @@ __published:
   TTBXItem *TBXItem251;
   TTBXItem *TBXItem252;
   TTBXItem *TBXItem253;
+  TTBXItem *TBXItem255;
+  TTBXSeparatorItem *TBXSeparatorItem65;
+  TTBXItem *TBXItem256;
   void __fastcall SplitterMoved(TObject *Sender);
   void __fastcall SplitterCanResize(TObject *Sender, int &NewSize,
     bool &Accept);

+ 2 - 0
source/forms/ScpExplorer.cpp

@@ -50,6 +50,8 @@ __fastcall TScpExplorerForm::TScpExplorerForm(TComponent* Owner)
   CopyPopup(BottomDock, TopDock);
   CopyPopup(LeftDock, TopDock);
   CopyPopup(RightDock, TopDock);
+  CopyPopup(QueueFileList, TopDock);
+  CopyPopup(QueueFileListSplitter, TopDock);
   reinterpret_cast<TLabel*>(RemotePanelSplitter)->OnDblClick = RemotePanelSplitterDblClick;
 
   QueuePanel->Parent = RemotePanel;

+ 14 - 0
source/forms/ScpExplorer.dfm

@@ -416,6 +416,9 @@ inherited ScpExplorerForm: TScpExplorerForm
           object TBXItem80: TTBXItem
             Action = NonVisualDataModule.QueueToolbarAction
           end
+          object TBXItem164: TTBXItem
+            Action = NonVisualDataModule.QueueFileListAction
+          end
           object TBXSeparatorItem22: TTBXSeparatorItem
           end
           object TBXSubmenuItem8: TTBXSubmenuItem
@@ -832,6 +835,11 @@ inherited ScpExplorerForm: TScpExplorerForm
           Action = NonVisualDataModule.QueueHideAction
           RadioItem = True
         end
+        object TBXSeparatorItem44: TTBXSeparatorItem
+        end
+        object TBXItem165: TTBXItem
+          Action = NonVisualDataModule.QueueFileListAction
+        end
       end
       object TBXItem71: TTBXItem
         Action = NonVisualDataModule.RemoteTreeAction
@@ -1122,12 +1130,18 @@ inherited ScpExplorerForm: TScpExplorerForm
     inherited QueueLabel: TPathLabel
       Width = 632
     end
+    inherited QueueFileListSplitter: TSplitter
+      Width = 632
+    end
     inherited QueueView3: TListView
       Width = 632
     end
     inherited QueueDock: TTBXDock
       Width = 632
     end
+    inherited QueueFileList: TListView
+      Width = 632
+    end
   end
   inherited SessionsPageControl: TThemePageControl
     Top = 209

+ 3 - 0
source/forms/ScpExplorer.h

@@ -322,6 +322,9 @@ __published:
   TTBXItem *TBXItem161;
   TTBXItem *TBXItem162;
   TTBXItem *TBXItem163;
+  TTBXItem *TBXItem164;
+  TTBXItem *TBXItem165;
+  TTBXSeparatorItem *TBXSeparatorItem44;
   void __fastcall RemoteDirViewUpdateStatusBar(TObject *Sender,
           const TStatusFileInfo &FileInfo);
   void __fastcall UnixPathComboBoxBeginEdit(TTBEditItem *Sender,

+ 17 - 0
source/windows/QueueController.cpp

@@ -634,3 +634,20 @@ bool __fastcall TQueueController::NeedRefresh()
   RememberConfiguration();
   return Result;
 }
+//---------------------------------------------------------------------------
+TQueueItemProxy * __fastcall TQueueController::GetFocusedPrimaryItem()
+{
+  TQueueItemProxy * Result = NULL;
+  TListItem * PrimaryItemOfFocused = FListView->ItemFocused;
+  if (PrimaryItemOfFocused != NULL)
+  {
+    while (!QueueViewItemToQueueItem(PrimaryItemOfFocused)->Info->Primary &&
+           DebugAlwaysTrue(PrimaryItemOfFocused->Index > 0))
+    {
+      PrimaryItemOfFocused = FListView->Items->Item[PrimaryItemOfFocused->Index - 1];
+    }
+    Result = QueueViewItemToQueueItem(PrimaryItemOfFocused);
+  }
+
+  return Result;
+}

+ 2 - 0
source/windows/QueueController.h

@@ -27,6 +27,8 @@ public:
 
   bool __fastcall NeedRefresh();
 
+  TQueueItemProxy * __fastcall GetFocusedPrimaryItem();
+
   __property TNotifyEvent OnChange = { read = FOnChange, write = FOnChange };
   __property bool Empty = { read = GetEmpty };
 

+ 6 - 0
source/windows/WinConfiguration.cpp

@@ -652,6 +652,9 @@ void __fastcall TWinConfiguration::Default()
   FQueueView.LastHideShow = qvHideWhenEmpty;
   FQueueView.ToolBar = true;
   FQueueView.Label = true;
+  FQueueView.FileList = false;
+  FQueueView.FileListHeight = 90;
+  FQueueView.FileListHeightPixelsPerInch = USER_DEFAULT_SCREEN_DPI;
 
   FEnableQueueByDefault = true;
 
@@ -1054,6 +1057,9 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool & SessionList)
     KEY(Integer,  QueueView.LastHideShow); \
     KEY(Bool,     QueueView.ToolBar); \
     KEY(Bool,     QueueView.Label); \
+    KEY(Bool,     QueueView.FileList); \
+    KEY(Integer,  QueueView.FileListHeight); \
+    KEY(Integer,  QueueView.FileListHeightPixelsPerInch); \
   ); \
   BLOCK(L"Interface\\Updates", CANCREATE, \
     KEY(Integer,  FUpdates.Period); \

+ 5 - 1
source/windows/WinConfiguration.h

@@ -143,8 +143,12 @@ struct TQueueViewConfiguration {
   TQueueViewShow LastHideShow;
   bool ToolBar;
   bool Label;
+  bool FileList;
+  int FileListHeight;
+  int FileListHeightPixelsPerInch;
   bool __fastcall operator !=(TQueueViewConfiguration & rhc)
-    { return C(Height) C(HeightPixelsPerInch) C(Layout) C(Show) C(LastHideShow) C(ToolBar) C(Label) 0; };
+    { return C(Height) C(HeightPixelsPerInch) C(Layout) C(Show) C(LastHideShow) C(ToolBar) C(Label)
+        C(FileList) C(FileListHeight) C(FileListHeightPixelsPerInch) 0; };
 };
 //---------------------------------------------------------------------------
 struct TUpdatesData