浏览代码

Bug 41: Show directory size in file panel

https://winscp.net/tracker/41

Source commit: 65720b4a0c4b3ff88c4b0c116de9f2b13f57480a
Martin Prikryl 4 年之前
父节点
当前提交
bfe5b292e9

+ 27 - 5
source/components/UnixDirView.cpp

@@ -234,7 +234,7 @@ __int64 __fastcall TUnixDirView::ItemFileSize(TListItem * Item)
 {
 #ifndef DESIGN_ONLY
   ASSERT_VALID_ITEM;
-  return ITEMFILE->Size;
+  return (ITEMFILE->CalculatedSize >= 0) ? ITEMFILE->CalculatedSize : ITEMFILE->Size;
 #else
   DebugUsedParam(Item);
   return 0;
@@ -353,7 +353,7 @@ void __fastcall TUnixDirView::LoadFiles()
         // Assignment is redundant
         Item = Items->AddItem(Item);
         Item->Caption = File->FileName;
-        if (FFullLoad)
+        if (DebugAlwaysFalse(FFullLoad))
         {
           // this is out of date
           // (missing columns and does not update then file properties are loaded)
@@ -388,10 +388,20 @@ void __fastcall TUnixDirView::GetDisplayInfo(TListItem * Item, tagLVITEMW &DispI
       switch (DispInfo.iSubItem) {
         case uvName: Value = File->FileName; break;
         case uvSize:
-          // expanded from ?: to avoid memory leaks
-          if (!File->IsDirectory)
           {
-            Value = FormatPanelBytes(File->Size, FormatSizeBytes);
+            __int64 Size;
+            if (!File->IsDirectory)
+            {
+              Size = File->Size;
+            }
+            else
+            {
+              Size = File->CalculatedSize;
+            }
+            if (Size >= 0)
+            {
+              Value = FormatPanelBytes(Size, FormatSizeBytes);
+            }
           }
           break;
         case uvChanged: Value = File->UserModificationStr; break;
@@ -1075,3 +1085,15 @@ void __fastcall TUnixDirView::UpdatePathLabelCaption()
     PathLabel->Mask = UnicodeString();
   }
 }
+//---------------------------------------------------------------------------
+void __fastcall TUnixDirView::SetItemCalculatedSize(TListItem * Item, __int64 Size)
+{
+  __int64 OldSize;
+  #ifndef DESIGN_ONLY
+  OldSize = ITEMFILE->CalculatedSize;
+  ITEMFILE->CalculatedSize = Size;
+  #else
+  OldSize = -1;
+  #endif
+  ItemCalculatedSizeUpdated(Item, OldSize, Size);
+}

+ 1 - 0
source/components/UnixDirView.h

@@ -96,6 +96,7 @@ public:
   virtual bool __fastcall ItemIsParentDirectory(TListItem * Item);
   virtual UnicodeString __fastcall ItemFullFileName(TListItem * Item);
   virtual __int64 __fastcall ItemFileSize(TListItem * Item);
+  virtual void __fastcall SetItemCalculatedSize(TListItem * Item, __int64 Size);
   virtual bool __fastcall PasteFromClipBoard(UnicodeString TargetPath = L"");
   void __fastcall UpdateFiles();
   void __fastcall DisplayContextMenu(const TPoint &Where);

+ 2 - 0
source/core/RemoteFiles.cpp

@@ -820,6 +820,7 @@ __fastcall TRemoteFile::TRemoteFile(TRemoteFile * ALinkedByFile):
   FDirectory = NULL;
   FIsHidden = -1;
   FIsEncrypted = false;
+  FCalculatedSize = -1;
 }
 //---------------------------------------------------------------------------
 __fastcall TRemoteFile::~TRemoteFile()
@@ -845,6 +846,7 @@ TRemoteFile * __fastcall TRemoteFile::Duplicate(bool Standalone) const
     COPY_FP(Owner);
     COPY_FP(ModificationFmt);
     COPY_FP(Size);
+    COPY_FP(CalculatedSize);
     COPY_FP(FileName);
     COPY_FP(DisplayName);
     COPY_FP(INodeBlocks);

+ 2 - 0
source/core/RemoteFiles.h

@@ -84,6 +84,7 @@ private:
   TRemoteToken FOwner;
   TModificationFmt FModificationFmt;
   __int64 FSize;
+  __int64 FCalculatedSize;
   UnicodeString FFileName;
   UnicodeString FDisplayName;
   Integer FINodeBlocks;
@@ -153,6 +154,7 @@ public:
   __property TRemoteFileList * Directory = { read = FDirectory, write = FDirectory };
   __property UnicodeString RightsStr = { read = GetRightsStr };
   __property __int64 Size = { read = GetSize, write = FSize };
+  __property __int64 CalculatedSize = { read = FCalculatedSize, write = FCalculatedSize };
   __property TRemoteToken Owner = { read = FOwner, write = FOwner };
   __property TRemoteToken Group = { read = FGroup, write = FGroup };
   __property UnicodeString FileName = { read = FFileName, write = FFileName };

+ 15 - 15
source/core/Terminal.cpp

@@ -4505,7 +4505,7 @@ bool __fastcall TTerminal::DoCalculateDirectorySize(const UnicodeString & FileNa
   {
     try
     {
-      ProcessDirectory(FileName, CalculateFileSize, Params);
+      ProcessDirectory(FileName, CalculateFileSize, Params, Params->UseCache);
       Result = true;
     }
     catch(Exception & E)
@@ -4534,9 +4534,7 @@ bool __fastcall TTerminal::DoCalculateDirectorySize(const UnicodeString & FileNa
   return Result;
 }
 //---------------------------------------------------------------------------
-bool __fastcall TTerminal::CalculateFilesSize(TStrings * FileList,
-  __int64 & Size, int Params, const TCopyParamType * CopyParam,
-  bool AllowDirs, TCalculateSizeStats & Stats)
+bool TTerminal::CalculateFilesSize(TStrings * FileList, __int64 & Size, TCalculateSizeParams & Params)
 {
   // With FTP protocol, we may use DSIZ command from
   // draft-peterson-streamlined-ftp-command-extensions-10
@@ -4545,18 +4543,13 @@ bool __fastcall TTerminal::CalculateFilesSize(TStrings * FileList,
   TValueRestorer<bool> UseBusyCursorRestorer(FUseBusyCursor);
   FUseBusyCursor = false;
 
-  TCalculateSizeParams Param;
-  Param.Params = Params;
-  Param.CopyParam = CopyParam;
-  Param.Stats = &Stats;
-  Param.AllowDirs = AllowDirs;
-  ProcessFiles(FileList, foCalculateSize, DoCalculateFileSize, &Param);
-  Size = Param.Size;
+  ProcessFiles(FileList, foCalculateSize, DoCalculateFileSize, &Params);
+  Size = Params.Size;
   if (Configuration->ActualLogProtocol >= 1)
   {
     LogEvent(FORMAT(L"Size of %d remote files/folders calculated as %s", (FileList->Count, IntToStr(Size))));
   }
-  return Param.Result;
+  return Params.Result;
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminal::CalculateFilesChecksum(const UnicodeString & Alg,
@@ -6350,7 +6343,10 @@ void __fastcall TTerminal::SynchronizeChecklistCalculateSize(
       __int64 RemoteSize = 0;
       TCalculateSizeStats RemoteStats;
       RemoteStats.CalculatedSizes = &RemoteCalculatedSizes;
-      CalculateFilesSize(RemoteFileList.get(), RemoteSize, 0, CopyParam, true, RemoteStats);
+      TCalculateSizeParams Params;
+      Params.CopyParam = CopyParam;
+      Params.Stats = &RemoteStats;
+      CalculateFilesSize(RemoteFileList.get(), RemoteSize, Params);
     }
   }
   __finally
@@ -7374,8 +7370,12 @@ bool __fastcall TTerminal::CopyToLocal(
       {
         TCalculateSizeStats Stats;
         Stats.FoundFiles = Files.get();
-        bool CalculateSize = ACanParallel || CopyParam->CalculateSize;
-        if (CalculateFilesSize(FilesToCopy, TotalSize, csIgnoreErrors, CopyParam, CalculateSize, Stats))
+        TCalculateSizeParams Params;
+        Params.Params = csIgnoreErrors;
+        Params.CopyParam = CopyParam;
+        Params.AllowDirs = ACanParallel || CopyParam->CalculateSize;
+        Params.Stats = &Stats;
+        if (CalculateFilesSize(FilesToCopy, TotalSize, Params))
         {
           TotalSizeKnown = true;
         }

+ 8 - 4
source/core/Terminal.h

@@ -563,9 +563,7 @@ public:
     /*const TMoveFileParams*/ void * Param);
   bool __fastcall CopyFiles(TStrings * FileList, const UnicodeString Target,
     const UnicodeString FileMask);
-  bool __fastcall CalculateFilesSize(TStrings * FileList, __int64 & Size,
-    int Params, const TCopyParamType * CopyParam, bool AllowDirs,
-    TCalculateSizeStats & Stats);
+  bool CalculateFilesSize(TStrings * FileList, __int64 & Size, TCalculateSizeParams & Params);
   bool __fastcall CalculateLocalFilesSize(TStrings * FileList, __int64 & Size,
     const TCopyParamType * CopyParam, bool AllowDirs, TStrings * Files, TCalculatedSizes * CalculatedSizes);
   void __fastcall CalculateFilesChecksum(const UnicodeString & Alg, TStrings * FileList,
@@ -729,15 +727,21 @@ struct TCalculateSizeStats
 //---------------------------------------------------------------------------
 struct TCalculateSizeParams
 {
+friend class TTerminal;
+
+public:
   TCalculateSizeParams();
 
-  __int64 Size;
   int Params;
   const TCopyParamType * CopyParam;
   TCalculateSizeStats * Stats;
   bool AllowDirs;
+  bool UseCache;
+
+private:
   TCollectedFileList * Files;
   UnicodeString LastDirPath;
+  __int64 Size;
   bool Result;
 };
 //---------------------------------------------------------------------------

+ 125 - 28
source/forms/CustomScpExplorer.cpp

@@ -165,6 +165,7 @@ __fastcall TCustomScpExplorerForm::TCustomScpExplorerForm(TComponent* Owner):
   RemoteDirView->Invalidate();
   FAutoOperation = false;
   FOnFileOperationFinished = NULL;
+  FPrimaryOperation = foNone;
   FForceExecution = false;
   FIgnoreNextDialogChar = 0;
   FErrorList = NULL;
@@ -207,6 +208,7 @@ __fastcall TCustomScpExplorerForm::TCustomScpExplorerForm(TComponent* Owner):
   FIncrementalSearchHaveNext = false;
   FQueueFileList.reset(new TQueueFileList());
   FProgressSide = osCurrent;
+  FCalculateSizeOperation = NULL;
 
   FEditorManager = new TEditorManager();
   FEditorManager->OnFileChange = ExecutedFileChanged;
@@ -1232,13 +1234,13 @@ bool __fastcall TCustomScpExplorerForm::CopyParamDialog(
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::ClearTransferSourceSelection(TTransferDirection Direction)
 {
+  TOperationSide Side = ((Direction == tdToRemote) ? osLocal : osRemote);
   if (FOnFileOperationFinished != NULL)
   {
-    FOnFileOperationFinished(UnicodeString(), true);
+    FOnFileOperationFinished(Side, UnicodeString(), true);
   }
   else
   {
-    TOperationSide Side = ((Direction == tdToRemote) ? osLocal : osRemote);
     if (HasDirView[Side])
     {
       DirView(Side)->SelectAll(smNone);
@@ -1620,6 +1622,23 @@ bool __fastcall TCustomScpExplorerForm::PanelOperation(TOperationSide /*Side*/,
     (DropSourceControl == DirView(osOther));
 }
 //---------------------------------------------------------------------------
+TListItem * TCustomScpExplorerForm::VisualiseOperationFinished(TOperationSide Side, const UnicodeString & FileName, bool Unselect)
+{
+  TCustomDirView * ADirView = DirView(Side);
+  UnicodeString FileNameOnly = ExtractFileName(FileName, !IsSideLocalBrowser(Side));
+  TListItem * Item = ADirView->FindFileItem(FileNameOnly);
+  // this can happen when local drive is unplugged in the middle of the operation
+  if (Item != NULL)
+  {
+    if (Unselect)
+    {
+      Item->Selected = false;
+    }
+    ADirView->MakeProgressVisible(Item);
+  }
+  return Item;
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::DoOperationFinished(
   TFileOperation Operation, TOperationSide Side,
   bool /*Temp*/, const UnicodeString & FileName, bool Success,
@@ -1629,36 +1648,28 @@ void __fastcall TCustomScpExplorerForm::DoOperationFinished(
   {
     // no selection on "/upload", form serves only as event handler
     // (it is not displayed)
-    if (PanelOperation(Side, FDragDropOperation) &&
-        Visible && (Operation != foCalculateSize) &&
-        (Operation != foGetProperties) && (Operation != foCalculateChecksum))
+    if (PanelOperation(Side, FDragDropOperation) && Visible &&
+        ((Operation == FPrimaryOperation) ||
+         ((Operation != foCalculateSize) && (Operation != foGetProperties) && (Operation != foCalculateChecksum))))
     {
+      TOperationSide DViewSide;
+      if ((FProgressSide != osCurrent) &&
+          IsLocalBrowserMode() && (Side == osLocal)) // Only to limit the impact
+      {
+        // assume the operation is over the focused panel
+        DViewSide = FProgressSide;
+      }
+      else
+      {
+        DViewSide = Side;
+      }
       if (FOnFileOperationFinished != NULL)
       {
-        FOnFileOperationFinished(FileName, Success);
+        FOnFileOperationFinished(DViewSide, FileName, Success);
       }
       else
       {
-        TOperationSide DViewSide;
-        if ((FProgressSide != osCurrent) &&
-            IsLocalBrowserMode() && (Side == osLocal)) // Only to limit the impact
-        {
-          // assume the operation is over the focused panel
-          DViewSide = FProgressSide;
-        }
-        else
-        {
-          DViewSide = Side;
-        }
-        TCustomDirView * DView = DirView(DViewSide);
-        UnicodeString FileNameOnly = ExtractFileName(FileName, !IsSideLocalBrowser(DViewSide));
-        TListItem *Item = DView->FindFileItem(FileNameOnly);
-        // this can happen when local drive is unplugged in the middle of the operation
-        if (Item != NULL)
-        {
-          if (Success) Item->Selected = false;
-          DView->MakeProgressVisible(Item);
-        }
+        VisualiseOperationFinished(DViewSide, FileName, Success);
       }
     }
 
@@ -4355,7 +4366,9 @@ void __fastcall TCustomScpExplorerForm::CalculateSize(
   {
     try
     {
-      Terminal->CalculateFilesSize(FileList, Size, 0, NULL, true, Stats);
+      TCalculateSizeParams Params;
+      Params.Stats = &Stats;
+      Terminal->CalculateFilesSize(FileList, Size, Params);
     }
     catch(...)
     {
@@ -10028,7 +10041,7 @@ void __fastcall TCustomScpExplorerForm::DoEditFoundFiles(
       UnicodeString FileName = FileList->Strings[Index];
       TRemoteFile * File = static_cast<TRemoteFile *>(FileList->Objects[Index]);
       ExecuteRemoteFile(FileName, File, efDefaultEditor);
-      OnFileOperationFinished(FileName, true);
+      OnFileOperationFinished(osRemote, FileName, true);
     }
   }
 }
@@ -11328,3 +11341,87 @@ void __fastcall TCustomScpExplorerForm::SessionsPageControlTabHint(TPageControl
   }
 }
 //---------------------------------------------------------------------------
+typedef std::vector<TListItem *> TListItemsVector;
+struct TCalculateSizeOperation
+{
+  TCalculateSizeStats Stats;
+  TListItemsVector ListItems;
+  size_t Index;
+};
+//---------------------------------------------------------------------------
+void TCustomScpExplorerForm::CalculateDirectorySizes()
+{
+  TCalculateSizeOperation CalculateSizeOperation;
+  TOperationSide Side = GetSide(osCurrent);
+  TCustomDirView * ADirView = DirView(Side);
+  bool FullPath = IsSideLocalBrowser(Side);
+  std::unique_ptr<TStrings> AllFileList(ADirView->CreateFileList(false, FullPath, NULL, true));
+  std::unique_ptr<TStrings> FileList(new TStringList());
+  for (int Index = 0; Index < AllFileList->Count; Index++)
+  {
+    TListItem * Item = DebugNotNull(dynamic_cast<TListItem *>(AllFileList->Objects[Index]));
+    if (ADirView->ItemIsDirectory(Item))
+    {
+      FileList->AddObject(AllFileList->Strings[Index], ADirView->ItemData(Item));
+      CalculateSizeOperation.ListItems.push_back(Item);
+    }
+  }
+
+  CalculateSizeOperation.Index = 0;
+
+  __int64 Size = 0;
+  TValueRestorer<TCalculateSizeOperation *> DirectorySizeOperationRestorer(FCalculateSizeOperation);
+  FCalculateSizeOperation = &CalculateSizeOperation;
+
+  TValueRestorer<TFileOperationFinishedEvent> OnFileOperationFinishedRestorer(FOnFileOperationFinished);
+  FOnFileOperationFinished = DirectorySizeCalculated;
+  TValueRestorer<TFileOperation> PrimaryOperationRestorer(FPrimaryOperation);
+  FPrimaryOperation = foCalculateSize;
+
+  TCalculatedSizes CalculatedSizes;
+  CalculateSizeOperation.Stats.CalculatedSizes = &CalculatedSizes;
+
+  if (Side == osLocal)
+  {
+    Terminal->CalculateLocalFilesSize(FileList.get(), Size, NULL, true, NULL, &CalculatedSizes);
+  }
+  else
+  {
+    TCalculateSizeParams Params;
+    Params.Stats = &CalculateSizeOperation.Stats;
+    Params.UseCache = true;
+    Terminal->CalculateFilesSize(FileList.get(), Size, Params);
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::DirectorySizeCalculated(TOperationSide Side, const UnicodeString & FileName, bool)
+{
+  DebugAlwaysTrue(FCalculateSizeOperation != NULL);
+
+  TListItem * Item = VisualiseOperationFinished(Side, FileName, false);
+  TCustomDirView * ADirView = dynamic_cast<TCustomDirView *>(Item->Owner->Owner);
+  // optimization
+  bool GotExpectedItem =
+    (FCalculateSizeOperation->Index < FCalculateSizeOperation->ListItems.size()) &&
+    (FCalculateSizeOperation->ListItems[FCalculateSizeOperation->Index] == Item);
+  int Index = -1;
+  if (DebugAlwaysTrue(GotExpectedItem))
+  {
+    Index = FCalculateSizeOperation->Index;
+    FCalculateSizeOperation->Index++;
+  }
+  else
+  {
+    TListItemsVector::iterator I =
+      std::find(FCalculateSizeOperation->ListItems.begin(), FCalculateSizeOperation->ListItems.end(), Item);
+    if (DebugAlwaysTrue(I != FCalculateSizeOperation->ListItems.end()))
+    {
+      Index = I - FCalculateSizeOperation->ListItems.begin();
+    }
+  }
+  if (DebugAlwaysTrue(Index >= 0))
+  {
+    __int64 Size = (*FCalculateSizeOperation->Stats.CalculatedSizes)[Index];
+    ADirView->SetItemCalculatedSize(Item, Size);
+  }
+}

+ 6 - 0
source/forms/CustomScpExplorer.h

@@ -50,6 +50,7 @@ class ITaskbarList3;
 struct TSynchronizeParams;
 class TBookmark;
 class TManagedTerminal;
+class TCalculateSizeOperation;
 //---------------------------------------------------------------------------
 enum TActionAllowed { aaShortCut, aaUpdate, aaExecute };
 enum TActionFlag { afLocal = 1, afRemote = 2, afExplorer = 4, afCommander = 8 };
@@ -230,6 +231,7 @@ private:
   bool FFormRestored;
   bool FAutoOperation;
   TFileOperationFinishedEvent FOnFileOperationFinished;
+  TFileOperation FPrimaryOperation;
   bool FForceExecution;
   unsigned short FIgnoreNextDialogChar;
   TStringList * FErrorList;
@@ -298,6 +300,7 @@ private:
   std::auto_ptr<TQueueFileList> FQueueFileList;
   bool FStarted;
   bool FUpdatingSessionTabs;
+  TCalculateSizeOperation * FCalculateSizeOperation;
 
   bool __fastcall GetEnableFocusedOperation(TOperationSide Side, int FilesOnly);
   bool __fastcall GetEnableSelectedOperation(TOperationSide Side, int FilesOnly);
@@ -729,6 +732,8 @@ protected:
   virtual UnicodeString GetTabHintDetails(TManagedTerminal * ASession);
   virtual UnicodeString GetNewTabHintDetails();
   UnicodeString GetSessionPath(TManagedTerminal * ASession, TOperationSide Side);
+  void __fastcall DirectorySizeCalculated(TOperationSide Side, const UnicodeString & FileName, bool Success);
+  TListItem * VisualiseOperationFinished(TOperationSide Side, const UnicodeString & FileName, bool Unselect);
 
 public:
   virtual __fastcall ~TCustomScpExplorerForm();
@@ -876,6 +881,7 @@ public:
   virtual UnicodeString GetLocalBrowserSessionTitle(TManagedTerminal * Session);
   virtual int GetNewTabActionImageIndex();
   virtual int GetNewTabTabImageIndex(TOperationSide Side);
+  void CalculateDirectorySizes();
 
   __property bool ComponentVisible[Byte Component] = { read = GetComponentVisible, write = SetComponentVisible };
   __property bool EnableFocusedOperation[TOperationSide Side] = { read = GetEnableFocusedOperation, index = 0 };

+ 2 - 2
source/forms/FileFind.cpp

@@ -614,7 +614,7 @@ TListItem * __fastcall TFileFindDialog::FileOperationFinished(const UnicodeStrin
   return Result;
 }
 //---------------------------------------------------------------------------
-void __fastcall TFileFindDialog::FileDeleteFinished(const UnicodeString & FileName, bool Success)
+void __fastcall TFileFindDialog::FileDeleteFinished(TOperationSide, const UnicodeString & FileName, bool Success)
 {
   // Delete in queue not supported
   DebugAssert(!FileName.IsEmpty());
@@ -626,7 +626,7 @@ void __fastcall TFileFindDialog::FileDeleteFinished(const UnicodeString & FileNa
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TFileFindDialog::FileDownloadFinished(const UnicodeString & FileName, bool Success)
+void __fastcall TFileFindDialog::FileDownloadFinished(TOperationSide, const UnicodeString & FileName, bool Success)
 {
   if (FileName.IsEmpty())
   {

+ 2 - 2
source/forms/FileFind.h

@@ -132,8 +132,8 @@ private:
   void __fastcall CMDialogKey(TWMKeyDown & Message);
   void __fastcall CMDpiChanged(TMessage & Message);
   void __fastcall ClearItem(TListItem * Item);
-  void __fastcall FileDeleteFinished(const UnicodeString & FileName, bool Success);
-  void __fastcall FileDownloadFinished(const UnicodeString & FileName, bool Success);
+  void __fastcall FileDeleteFinished(TOperationSide Side, const UnicodeString & FileName, bool Success);
+  void __fastcall FileDownloadFinished(TOperationSide Side, const UnicodeString & FileName, bool Success);
   TListItem * __fastcall FileOperationFinished(const UnicodeString & FileName);
   void __fastcall FileListOperation(TFileListOperationEvent Operation, TFileOperationFinishedEvent OnFileOperationFinished);
   TIEListViewColProperties * GetColProperties();

+ 2 - 0
source/forms/NonVisual.cpp

@@ -197,6 +197,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
   UPD(UnlockAction,
     EnabledSelectedOperation && !ScpExplorer->IsSideLocalBrowser(osCurrent) &&
     ScpExplorer->Terminal->IsCapable[fcLocking])
+  UPD(CalculateDirectorySizesAction, EnabledSelectedOperation)
   // local selected operation
   UPD(LocalCopyAction, HasTerminal && EnabledLocalSelectedOperation)
   UPDEX1(LocalCopyQueueAction,
@@ -557,6 +558,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
     EXE(FileListFromClipboardAction, ScpExplorer->FileListFromClipboard())
     EXE(LockAction, ScpExplorer->ExecuteFileOperationCommand(foLock, osCurrent, false))
     EXE(UnlockAction, ScpExplorer->ExecuteFileOperationCommand(foUnlock, osCurrent, false))
+    EXE(CalculateDirectorySizesAction, ScpExplorer->CalculateDirectorySizes())
     // local selected operation
     EXE(LocalCopyAction, ScpExplorer->ExecuteCopyOperationCommand(osLocal, false, ShortCutFlag))
     EXE(LocalCopyQueueAction, ScpExplorer->ExecuteCopyOperationCommand(osLocal, false, cocQueue))

+ 10 - 0
source/forms/NonVisual.dfm

@@ -2385,6 +2385,16 @@ object NonVisualDataModule: TNonVisualDataModule
         'When turned on, the New Tab command opens a new remote tab. Othe' +
         'rwise it opens a new local tab.'
     end
+    object CalculateDirectorySizesAction: TAction
+      Tag = 15
+      Category = 'Selected Operation'
+      Caption = '&Calculate Directory Sizes'
+      HelpKeyword = 'ui_file_panel#directory_sizes'
+      Hint = 
+        'Calculate sizes of the selected directories and display them in ' +
+        'the file panel'
+      ShortCut = 40973
+    end
   end
   object ExplorerBarPopup: TTBXPopupMenu
     Images = GlyphsModule.ExplorerImages

+ 1 - 0
source/forms/NonVisual.h

@@ -689,6 +689,7 @@ __published:    // IDE-managed Components
   TTBXItem *TBXItem108;
   TTBXSeparatorItem *TBXSeparatorItem19;
   TTBXItem *TBXItem111;
+  TAction *CalculateDirectorySizesAction;
   void __fastcall ExplorerActionsUpdate(TBasicAction *Action, bool &Handled);
   void __fastcall ExplorerActionsExecute(TBasicAction *Action, bool &Handled);
   void __fastcall SessionIdleTimerTimer(TObject *Sender);

+ 3 - 0
source/forms/ScpCommander.dfm

@@ -295,6 +295,9 @@ inherited ScpCommanderForm: TScpCommanderForm
         object TBXItem41: TTBXItem
           Action = NonVisualDataModule.CurrentPropertiesAction
         end
+        object TBXItem239: TTBXItem
+          Action = NonVisualDataModule.CalculateDirectorySizesAction
+        end
       end
       object TBXSubmenuItem7: TTBXSubmenuItem
         Caption = '&Commands'

+ 1 - 0
source/forms/ScpCommander.h

@@ -452,6 +452,7 @@ __published:
   TTBXItem *TBXItem232;
   TTBXSeparatorItem *TBXSeparatorItem68;
   TTBXItem *TBXItem238;
+  TTBXItem *TBXItem239;
   void __fastcall SplitterMoved(TObject *Sender);
   void __fastcall SplitterCanResize(TObject *Sender, int &NewSize,
     bool &Accept);

+ 15 - 4
source/packages/filemng/CustomDirView.pas

@@ -273,7 +273,6 @@ type
     function ItemCanDrag(Item: TListItem): Boolean; virtual;
     function DoItemColor(Item: TListItem): TColor;
     function ItemColor(Item: TListItem): TColor; virtual;
-    function ItemData(Item: TListItem): TObject; virtual;
     function ItemImageIndex(Item: TListItem; Cache: Boolean): Integer; virtual; abstract;
     // ItemIsDirectory and ItemFullFileName is in public block
     function ItemIsRecycleBin(Item: TListItem): Boolean; virtual;
@@ -339,6 +338,7 @@ type
     procedure DoUpdateStatusBar(Force: Boolean = False);
     procedure DoCustomDrawItem(Item: TListItem; Stage: TCustomDrawStage);
     procedure RestoreFocus(FocusedItem: string);
+    procedure ItemCalculatedSizeUpdated(Item: TListItem; OldSize, NewSize: Int64);
     property ImageList16: TImageList read FImageList16;
     property ImageList32: TImageList read FImageList32;
   public
@@ -346,7 +346,8 @@ type
     destructor Destroy; override;
     procedure Reload(CacheIcons: Boolean); virtual;
     function CreateFocusedFileList(FullPath: Boolean; FileList: TStrings = nil): TStrings;
-    function CreateFileList(Focused: Boolean; FullPath: Boolean; FileList: TStrings = nil): TStrings;
+    function CreateFileList(Focused: Boolean; FullPath: Boolean; FileList: TStrings = nil;
+      ItemObject: Boolean = False): TStrings;
     function AnyFileSelected(OnlyFocused: Boolean; FilesOnly: Boolean;
       FocusedFileOnlyWhenFocused: Boolean): Boolean;
     procedure SelectFiles(Filter: TFileFilter; Select: Boolean);
@@ -363,6 +364,8 @@ type
     function ItemFileName(Item: TListItem): string; virtual; abstract;
     function ItemFileSize(Item: TListItem): Int64; virtual; abstract;
     function ItemFileTime(Item: TListItem; var Precision: TDateTimePrecision): TDateTime; virtual; abstract;
+    function ItemData(Item: TListItem): TObject; virtual;
+    procedure SetItemCalculatedSize(Item: TListItem; Size: Int64); virtual; abstract;
     procedure ReloadDirectory; virtual; abstract;
     procedure DisplayPropertiesMenu; virtual; abstract;
     function CreateChangedFileList(DirView: TCustomDirView; FullPath: Boolean;
@@ -2352,9 +2355,9 @@ begin
 end;
 
 function TCustomDirView.CreateFileList(Focused: Boolean; FullPath: Boolean;
-  FileList: TStrings): TStrings;
+  FileList: TStrings; ItemObject: Boolean): TStrings;
 begin
-  Result := CustomCreateFileList(Focused, False, FullPath, FileList);
+  Result := CustomCreateFileList(Focused, False, FullPath, FileList, ItemObject);
 end;
 
 procedure TCustomDirView.DDDrop(DataObj: IDataObject; grfKeyState: Integer;
@@ -3436,6 +3439,14 @@ begin
     end;
 end;
 
+procedure TCustomDirView.ItemCalculatedSizeUpdated(Item: TListItem; OldSize, NewSize: Int64);
+begin
+  if OldSize >= 0 then Dec(FFilesSize, OldSize);
+  if NewSize >= 0 then Inc(FFilesSize, NewSize);
+  Item.Update;
+  UpdateStatusBar;
+end;
+
 initialization
   DropSourceControl := nil;
 

+ 25 - 2
source/packages/filemng/DirView.pas

@@ -81,6 +81,7 @@ type
     Attr: LongWord;
     FileTime: TFileTime;
     PIDL: PItemIDList; {Fully qualified PIDL}
+    CalculatedSize: Int64;
   end;
 
   {Record for fileinfo caching:}
@@ -270,6 +271,7 @@ type
     function ItemFileName(Item: TListItem): string; override;
     function ItemFileSize(Item: TListItem): Int64; override;
     function ItemFileTime(Item: TListItem; var Precision: TDateTimePrecision): TDateTime; override;
+    procedure SetItemCalculatedSize(Item: TListItem; ASize: Int64); override;
     procedure OpenFallbackPath(Value: string);
 
     {Thread handling: }
@@ -1063,6 +1065,7 @@ begin
     IconEmpty := True;
     if Size > 0 then Inc(FFilesSize, Size);
     PIDL := nil;
+    CalculatedSize := -1;
 
     // Need to add before assigning to .Caption and .OverlayIndex,
     // as the setters these call back to owning view.
@@ -2458,6 +2461,7 @@ procedure TDirView.GetDisplayInfo(ListItem: TListItem;
   var DispInfo: TLVItem);
 var
   Value: string;
+  ASize: Int64;
 begin
   Assert(Assigned(ListItem) and Assigned(ListItem.Data));
   with PFileRec(ListItem.Data)^, DispInfo  do
@@ -2497,7 +2501,11 @@ begin
       begin
         case TDirViewCol(iSubItem) of
           dvSize: {Size:     }
-            if not IsDirectory then Value := FormatPanelBytes(Size, FormatSizeBytes);
+            begin
+              if not IsDirectory then ASize := Size
+                else ASize := CalculatedSize;
+              if ASize >= 0 then Value := FormatPanelBytes(ASize, FormatSizeBytes);
+            end;
           dvType: {FileType: }
             Value := TypeName;
           dvChanged: {Date}
@@ -2947,7 +2955,9 @@ begin
   Result := 0;
   if Assigned(Item) and Assigned(Item.Data) then
     with PFileRec(Item.Data)^ do
-      if Size >= 0 then Result := Size;
+      if Size >= 0 then Result := Size
+        else
+      if CalculatedSize >= 0 then Result := CalculatedSize;
 end;
 
 function TDirView.ItemFileTime(Item: TListItem;
@@ -3441,6 +3451,19 @@ begin
     end;
 end;
 
+procedure TDirView.SetItemCalculatedSize(Item: TListItem; ASize: Int64);
+var
+  OldSize: Int64;
+begin
+  Assert(Assigned(Item) and Assigned(Item.Data));
+  with PFileRec(Item.Data)^ do
+  begin
+    OldSize := CalculatedSize;
+    CalculatedSize := ASize;
+  end;
+  ItemCalculatedSizeUpdated(Item, OldSize, ASize);
+end;
+
 {=================================================================}
 
 initialization

+ 1 - 1
source/windows/WinInterface.h

@@ -460,7 +460,7 @@ typedef void __fastcall (__closure *TFindEvent)
 typedef void __fastcall (__closure *TFocusFileEvent)
   (TTerminal * Terminal, const UnicodeString & Path);
 typedef void __fastcall (__closure *TFileOperationFinishedEvent)
-  (const UnicodeString & FileName, bool Success);
+  (TOperationSide Side, const UnicodeString & FileName, bool Success);
 typedef void __fastcall (__closure *TFileListOperationEvent)
   (TTerminal * Terminal, TStrings * FileList, TFileOperationFinishedEvent OnFileOperationFinished);
 void __fastcall ShowFileFindDialog(