Browse Source

Command to find Move candidate on Synchronization checklist window

Source commit: 3afc367ae03df30fa853586a997bfa6fc51ffe53
Martin Prikryl 1 year ago
parent
commit
e40c677e6e

+ 24 - 12
source/core/RemoteFiles.cpp

@@ -2884,20 +2884,32 @@ __int64 __fastcall TSynchronizeChecklist::TItem::GetSize(TAction AAction) const
   }
   else
   {
-    switch (AAction)
-    {
-      case saUploadNew:
-      case saUploadUpdate:
-        return Local.Size;
+    return GetBaseSize(AAction);
+  }
+}
+//---------------------------------------------------------------------------
+__int64 __fastcall TSynchronizeChecklist::TItem::GetBaseSize() const
+{
+  return GetBaseSize(Action);
+}
+//---------------------------------------------------------------------------
+__int64 __fastcall TSynchronizeChecklist::TItem::GetBaseSize(TAction AAction) const
+{
+  switch (AAction)
+  {
+    case saUploadNew:
+    case saUploadUpdate:
+    case saDeleteLocal:
+      return Local.Size;
 
-      case saDownloadNew:
-      case saDownloadUpdate:
-        return Remote.Size;
+    case saDownloadNew:
+    case saDownloadUpdate:
+    case saDeleteRemote:
+      return Remote.Size;
 
-      default:
-        DebugFail();
-        return 0;
-    }
+    default:
+      DebugFail();
+      return 0;
   }
 }
 //---------------------------------------------------------------------------

+ 2 - 0
source/core/RemoteFiles.h

@@ -496,6 +496,7 @@ public:
     bool IsRemoteOnly() const { return (Action == saDownloadNew) || (Action == saDeleteRemote); }
     bool IsLocalOnly() const { return (Action == saUploadNew) || (Action == saDeleteLocal); }
     bool HasSize() const { return !IsDirectory || FDirectoryHasSize; }
+    __int64 __fastcall GetBaseSize() const;
     __int64 __fastcall GetSize() const;
     __int64 __fastcall GetSize(TAction AAction) const;
 
@@ -506,6 +507,7 @@ public:
     bool FDirectoryHasSize;
 
     TItem();
+    __int64 __fastcall GetBaseSize(TAction AAction) const;
   };
 
   typedef std::vector<const TSynchronizeChecklist::TItem *> TItemList;

+ 274 - 20
source/forms/SynchronizeChecklist.cpp

@@ -20,6 +20,7 @@
 #include <TerminalManager.h>
 #include <System.IOUtils.hpp>
 #include <System.StrUtils.hpp>
+#include <algorithm>
 //---------------------------------------------------------------------
 #pragma link "IEListView"
 #pragma link "NortonLikeListView"
@@ -70,6 +71,7 @@ __fastcall TSynchronizeChecklistDialog::TSynchronizeChecklistDialog(
   FChangingItemIgnore = false;
   FChangingItemMass = false;
   FSynchronizing = false;
+  FMoveCandidatesValidForSort = MaxInt;
   FDirectories = 0;
 
   SelectScaledImageList(ActionImages);
@@ -85,6 +87,7 @@ __fastcall TSynchronizeChecklistDialog::TSynchronizeChecklistDialog(
   // button visibility cannot be bound to action visibility
   CustomCommandsButton2->Visible = CustomCommandsAction->Visible;
   MenuButton(CustomCommandsButton2);
+  MenuButton(ToolsMenuButton);
 }
 //---------------------------------------------------------------------
 __fastcall TSynchronizeChecklistDialog::~TSynchronizeChecklistDialog()
@@ -199,6 +202,7 @@ void __fastcall TSynchronizeChecklistDialog::UpdateControls()
   BrowseRemoteAction->Enabled = (ListView->SelCount == 1) && (SelectedItemAction != TSynchronizeChecklist::saDeleteLocal);
 
   SelectAllAction->Enabled = (ListView->SelCount < ListView->Items->Count) && !FSynchronizing;
+  FindMoveCandidateAction->Enabled = (ListView->Items->Count > 0) && !FSynchronizing;
 }
 //---------------------------------------------------------------------------
 bool __fastcall TSynchronizeChecklistDialog::GetWindowParams(UnicodeString & WindowParams)
@@ -886,6 +890,11 @@ void __fastcall TSynchronizeChecklistDialog::StatusBarMouseDown(
   }
 }
 //---------------------------------------------------------------------------
+TIEListViewColProperties * TSynchronizeChecklistDialog::GetColProperties()
+{
+  return dynamic_cast<TIEListViewColProperties *>(ListView->ColProperties);
+}
+//---------------------------------------------------------------------------
 void __fastcall TSynchronizeChecklistDialog::ListViewCompare(
   TObject * /*Sender*/, TListItem * Item1, TListItem * Item2, int /*Data*/,
   int & Compare)
@@ -893,9 +902,7 @@ void __fastcall TSynchronizeChecklistDialog::ListViewCompare(
   const TSynchronizeChecklist::TItem * ChecklistItem1 = GetChecklistItem(Item1);
   const TSynchronizeChecklist::TItem * ChecklistItem2 = GetChecklistItem(Item2);
 
-  TIEListViewColProperties * ColProperties =
-    dynamic_cast<TIEListViewColProperties *>(ListView->ColProperties);
-
+  TIEListViewColProperties * ColProperties = GetColProperties();
   switch (ColProperties->SortColumn)
   {
     case 0: // name
@@ -1241,6 +1248,28 @@ void __fastcall TSynchronizeChecklistDialog::CalculateSizeAllActionExecute(TObje
   CalculateSize(true);
 }
 //---------------------------------------------------------------------------
+TSynchronizeChecklist::TAction TSynchronizeChecklistDialog::GetOppositeMoveAction(TSynchronizeChecklist::TAction Action1)
+{
+  switch (Action1)
+  {
+    case TSynchronizeChecklist::saUploadNew:
+      return TSynchronizeChecklist::saDeleteRemote;
+    case TSynchronizeChecklist::saDownloadNew:
+      return TSynchronizeChecklist::saDeleteLocal;
+    case TSynchronizeChecklist::saDeleteRemote:
+      return TSynchronizeChecklist::saUploadNew;
+    case TSynchronizeChecklist::saDeleteLocal:
+      return TSynchronizeChecklist::saDownloadNew;
+    default:
+      return TSynchronizeChecklist::saNone;
+  }
+}
+//---------------------------------------------------------------------------
+bool TSynchronizeChecklistDialog::IsTransferNewAction(TSynchronizeChecklist::TAction Action)
+{
+  return (Action == TSynchronizeChecklist::saUploadNew) || (Action == TSynchronizeChecklist::saDownloadNew);
+}
+//---------------------------------------------------------------------------
 TSynchronizeChecklistDialog::TSynchronizeMoveItems __fastcall TSynchronizeChecklistDialog::GetMoveItems()
 {
   if ((ListView->SelCount != 2) || DebugAlwaysFalse(FSynchronizing))
@@ -1263,21 +1292,16 @@ TSynchronizeChecklistDialog::TSynchronizeMoveItems __fastcall TSynchronizeCheckl
       TSynchronizeChecklist::TAction Action1 = GetChecklistItemAction(ChecklistItem1);
       TSynchronizeChecklist::TAction Action2 = GetChecklistItemAction(ChecklistItem2);
 
-      if ((Action1 == TSynchronizeChecklist::saUploadNew) && (Action2 == TSynchronizeChecklist::saDeleteRemote))
-      {
-        return TSynchronizeMoveItems(ChecklistItem1, ChecklistItem2);
-      }
-      else if ((Action1 == TSynchronizeChecklist::saDownloadNew) && (Action2 == TSynchronizeChecklist::saDeleteLocal))
+      if (GetOppositeMoveAction(Action1) == Action2)
       {
-        return TSynchronizeMoveItems(ChecklistItem1, ChecklistItem2);
-      }
-      else if ((Action1 == TSynchronizeChecklist::saDeleteRemote) && (Action2 == TSynchronizeChecklist::saUploadNew))
-      {
-        return TSynchronizeMoveItems(ChecklistItem2, ChecklistItem1);
-      }
-      else if ((Action1 == TSynchronizeChecklist::saDeleteLocal) && (Action2 == TSynchronizeChecklist::saDownloadNew))
-      {
-        return TSynchronizeMoveItems(ChecklistItem2, ChecklistItem1);
+        if (IsTransferNewAction(Action1))
+        {
+          return TSynchronizeMoveItems(ChecklistItem1, ChecklistItem2);
+        }
+        else
+        {
+          return TSynchronizeMoveItems(ChecklistItem2, ChecklistItem1);
+        }
       }
       else
       {
@@ -1300,7 +1324,22 @@ void __fastcall TSynchronizeChecklistDialog::DeleteItem(TListItem * Item)
   FChecklistToListViewMap.erase(ChecklistItem);
 
   FChecklist->Delete(ChecklistItem);
-  ListView->Items->Delete(Item->Index);
+  int Index = Item->Index;
+  if (Item->Focused)
+  {
+    int FocusIndex = Index;
+    if (FocusIndex == ListView->Items->Count - 1)
+    {
+      FocusIndex--;
+    }
+    else
+    {
+      FocusIndex++;
+    }
+    ListView->ItemFocused = ListView->Items->Item[FocusIndex];
+  }
+  ListView->Items->Delete(Index);
+  FMoveCandidatesValidForSort = MaxInt; // can be optimized
 }
 //---------------------------------------------------------------------------
 void __fastcall TSynchronizeChecklistDialog::MoveActionExecute(TObject *)
@@ -1436,8 +1475,223 @@ void __fastcall TSynchronizeChecklistDialog::ListViewRecreate(TObject *)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TSynchronizeChecklistDialog::CalculateSizeButtonDropDownClick(TObject *)
+void __fastcall TSynchronizeChecklistDialog::ToolsMenuButtonClick(TObject *)
 {
-  MenuPopup(CalculateSizePopupMenu, CalculateSizeButton);
+  MenuPopup(ToolsPopupMenu, ToolsMenuButton);
+}
+//---------------------------------------------------------------------------
+void __fastcall TSynchronizeChecklistDialog::FindMoveCandidateActionExecute(TObject *)
+{
+  TIEListViewColProperties * ColProperties = GetColProperties();
+  int Sort = (ColProperties->SortAscending ? 1 : -1) * ColProperties->SortColumn;
+
+  if (FMoveCandidatesValidForSort != Sort)
+  {
+    FMoveCandidatesFileName.clear();
+    FMoveCandidatesSize.clear();
+    for (int Index = 0; Index < ListView->Items->Count; Index++)
+    {
+      const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(ListView->Items->Item[Index]);
+      if (ChecklistItem->IsLocalOnly() || ChecklistItem->IsRemoteOnly())
+      {
+        if (ChecklistItem->IsDirectory)
+        {
+          FMoveCandidatesFileName[ChecklistItem->GetFileName().LowerCase()].push_back(ChecklistItem);
+        }
+        else
+        {
+          FMoveCandidatesSize[ChecklistItem->GetBaseSize()].push_back(ChecklistItem);
+        }
+      }
+    }
+
+    FMoveCandidatesValidForSort = Sort;
+  }
+
+  bool Found = false;
+  if (!FMoveCandidatesFileName.empty() || !FMoveCandidatesSize.empty())
+  {
+    TListItem * ItemFocused = ListView->ItemFocused;
+    TListItem * Item;
+    if (ItemFocused != NULL)
+    {
+      Item = ItemFocused;
+    }
+    // Can we have a selection without focus?
+    else if (DebugAlwaysFalse(ListView->Selected != NULL))
+    {
+      Item = ListView->Selected;
+    }
+    else if (ListView->Items->Count > 0)
+    {
+      Item = ListView->Items->Item[0];
+    }
+    else
+    {
+      DebugFail(); // the action is disabled when there are no items
+      Item = NULL;
+    }
+
+    TListItem * FirstItem = NULL;
+
+    while (!Found && (Item != NULL))
+    {
+      const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item);
+      TSynchronizeChecklist::TAction OppositeAction = GetOppositeMoveAction(ChecklistItem->Action);
+      if ((OppositeAction != TSynchronizeChecklist::saNone) &&
+          // For focused item, we search pair even if the focused item is "delete" action,
+          // but when searching the next items, consider "transfer" actions only
+          ((Item == ItemFocused) || IsTransferNewAction(ChecklistItem->Action)))
+      {
+        TChecklistItems Candidates;
+        if (ChecklistItem->IsDirectory)
+        {
+          UnicodeString FileName = ChecklistItem->GetFileName().LowerCase();
+          TMoveCandidatesFileNameMap::const_iterator I = FMoveCandidatesFileName.find(FileName);
+          if (I != FMoveCandidatesFileName.end())
+          {
+            const TChecklistItems & NameCandidates = I->second;
+            for (size_t I = 0; I < NameCandidates.size(); I++)
+            {
+              const TSynchronizeChecklist::TItem * ChecklistItem2 = NameCandidates[I];
+              if ((ChecklistItem2->Action == OppositeAction) &&
+                  DebugAlwaysTrue(ChecklistItem2->IsDirectory))
+              {
+                Candidates.push_back(ChecklistItem2);
+              }
+            }
+          }
+        }
+        else
+        {
+          __int64 Size = ChecklistItem->GetBaseSize();
+          TMoveCandidatesSizeMap::const_iterator I = FMoveCandidatesSize.find(Size);
+          if (I != FMoveCandidatesSize.end())
+          {
+            UnicodeString FileName = ChecklistItem->GetFileName();
+            const TChecklistItems & SizeCandidates = I->second;
+            for (size_t I = 0; I < SizeCandidates.size(); I++)
+            {
+              const TSynchronizeChecklist::TItem * ChecklistItem2 = SizeCandidates[I];
+              if ((ChecklistItem2->Action == OppositeAction) &&
+                  DebugAlwaysTrue(!ChecklistItem2->IsDirectory))
+              {
+                bool IsCandidate;
+                // in addition to the same size, also the same file filename (although possibly different directory)
+                if (SameText(FileName, ChecklistItem2->GetFileName()))
+                {
+                  IsCandidate = true;
+                }
+                // or different filename but the same directory (in addition to the same size)
+                else if ((ChecklistItem->Action == TSynchronizeChecklist::saDeleteLocal) ||
+                         (ChecklistItem->Action == TSynchronizeChecklist::saDownloadNew))
+                {
+                  IsCandidate = SamePaths(ChecklistItem->Local.Directory, ChecklistItem2->Local.Directory);
+                }
+                else if ((ChecklistItem->Action == TSynchronizeChecklist::saDeleteRemote) ||
+                         (ChecklistItem->Action == TSynchronizeChecklist::saUploadNew))
+                {
+                  IsCandidate = UnixSamePath(ChecklistItem->Remote.Directory, ChecklistItem2->Remote.Directory);
+                }
+                else
+                {
+                  DebugFail();
+                  IsCandidate = false;
+                }
+
+                if (IsCandidate)
+                {
+                  Candidates.push_back(ChecklistItem2);
+                }
+              }
+            }
+          }
+        }
+
+        if (!Candidates.empty())
+        {
+          const TSynchronizeChecklist::TItem * ChecklistItem2 = Candidates[0];
+          if ((FirstItem == NULL) && Item->Selected && (ListView->SelCount == 2))
+          {
+            TListItem * NextSelected = ListView->GetNextItem(Item, sdAll, TItemStates() << isSelected);
+            if (NextSelected == NULL)
+            {
+              NextSelected = ListView->Selected; // Shorthand for GetNextItem(NULL, sdAll, isSelected)
+            }
+            TChecklistItems::const_iterator I = std::find(Candidates.begin(), Candidates.end(), GetChecklistItem(NextSelected));
+            if (I < Candidates.end() - 1)
+            {
+              ChecklistItem2 = *(I + 1);
+            }
+            else if (I == Candidates.end() - 1)
+            {
+              ChecklistItem2 = NULL;
+            }
+          }
+
+          if (ChecklistItem2 != NULL)
+          {
+            TListItem * Item2 = FChecklistToListViewMap[ChecklistItem2];
+
+            for (int Index = 0; Index < ListView->Items->Count; Index++)
+            {
+              TListItem * ItemI = ListView->Items->Item[Index];
+              ItemI->Selected = (ItemI == Item) || (ItemI == Item2);
+            }
+            ListView->ItemFocused = Item;
+
+            // IsItemVisible returns true even on partial visibility,
+            // so it is still worth trying MakeVisible to make them completelly visible
+            bool FlickerExpected = !ListView->IsItemVisible(Item) || !ListView->IsItemVisible(Item2);
+            if (FlickerExpected)
+            {
+              // does not seem to have any effect
+              DisableRedraw(ListView);
+            }
+            try
+            {
+              // even if the item turns to be invisible in the end, it at least scrolls the view towards it
+              Item2->MakeVisible(false);
+              // Should do minimal scroll needed to make the first item whole visible, but not hiding the second
+              Item->MakeVisible(false);
+            }
+            __finally
+            {
+              if (FlickerExpected)
+              {
+                EnableRedraw(ListView);
+              }
+            }
+
+            Found = true;
+          }
+        }
+      }
+
+      // Allow going through the first item twice, to make sure we roll over its previous pair candidates
+      if (Item == FirstItem)
+      {
+        Item = NULL;
+      }
+      else
+      {
+        if (FirstItem == NULL)
+        {
+          FirstItem = Item;
+        }
+
+        Item = ListView->GetNextItem(Item, sdAll, TItemStates());
+        if (Item == NULL)
+        {
+          Item = ListView->Items->Item[0];
+        }
+      }
+    }
+  }
+
+  if (!Found)
+  {
+    MessageBeep(MB_ICONHAND);
+  }
 }
 //---------------------------------------------------------------------------

+ 17 - 8
source/forms/SynchronizeChecklist.dfm

@@ -1445,7 +1445,7 @@ object SynchronizeChecklistDialog: TSynchronizeChecklistDialog
     end
     object CustomCommandsButton2: TButton
       Left = 8
-      Top = 353
+      Top = 322
       Width = 108
       Height = 25
       Action = CustomCommandsAction
@@ -1462,16 +1462,15 @@ object SynchronizeChecklistDialog: TSynchronizeChecklistDialog
       Anchors = [akLeft, akTop, akRight]
       TabOrder = 7
     end
-    object CalculateSizeButton: TButton
+    object ToolsMenuButton: TButton
       Left = 8
-      Top = 322
+      Top = 368
       Width = 108
       Height = 25
-      Action = CalculateSizeAction
       Anchors = [akLeft, akTop, akRight]
-      Style = bsSplitButton
+      Caption = '&Tools'
       TabOrder = 10
-      OnDropDownClick = CalculateSizeButtonDropDownClick
+      OnClick = ToolsMenuButtonClick
     end
     object MoveButton: TButton
       Left = 8
@@ -2193,6 +2192,11 @@ object SynchronizeChecklistDialog: TSynchronizeChecklistDialog
       Caption = 'Browse &Remote Directory'
       OnExecute = BrowseRemoteActionExecute
     end
+    object FindMoveCandidateAction: TAction
+      Caption = '&Find Move Candidate'
+      ShortCut = 49269
+      OnExecute = FindMoveCandidateActionExecute
+    end
   end
   object ActionImages120: TPngImageList
     Height = 20
@@ -3678,15 +3682,20 @@ object SynchronizeChecklistDialog: TSynchronizeChecklistDialog
     Left = 312
     Top = 416
   end
-  object CalculateSizePopupMenu: TPopupMenu
+  object ToolsPopupMenu: TPopupMenu
     Left = 88
     Top = 336
     object Calculate2: TMenuItem
       Action = CalculateSizeAction
-      Default = True
     end
     object CalculateAll2: TMenuItem
       Action = CalculateSizeAllAction
     end
+    object N4: TMenuItem
+      Caption = '-'
+    end
+    object FindMoveCandidate1: TMenuItem
+      Action = FindMoveCandidateAction
+    end
   end
 end

+ 16 - 3
source/forms/SynchronizeChecklist.h

@@ -55,7 +55,7 @@ __published:
   TPngImageList *ActionImages192;
   TAction *CalculateSizeAction;
   TMenuItem *Calculate1;
-  TButton *CalculateSizeButton;
+  TButton *ToolsMenuButton;
   TAction *CalculateSizeAllAction;
   TAction *MoveAction;
   TButton *MoveButton;
@@ -69,11 +69,14 @@ __published:
   TAction *BrowseRemoteAction;
   TMenuItem *BrowseLocalDirectory1;
   TMenuItem *BrowseLocalDirectory2;
+  TAction *FindMoveCandidateAction;
   TMenuItem *Calculate3;
   TMenuItem *CalculateAll1;
-  TPopupMenu *CalculateSizePopupMenu;
+  TPopupMenu *ToolsPopupMenu;
   TMenuItem *Calculate2;
   TMenuItem *CalculateAll2;
+  TMenuItem *N4;
+  TMenuItem *FindMoveCandidate1;
   void __fastcall HelpButtonClick(TObject * Sender);
   void __fastcall FormShow(TObject * Sender);
   void __fastcall StatusBarDrawPanel(TStatusBar *StatusBar,
@@ -113,7 +116,8 @@ __published:
   void __fastcall BrowseLocalActionExecute(TObject *Sender);
   void __fastcall BrowseRemoteActionExecute(TObject *Sender);
   void __fastcall ListViewRecreate(TObject *Sender);
-  void __fastcall CalculateSizeButtonDropDownClick(TObject *Sender);
+  void __fastcall ToolsMenuButtonClick(TObject *Sender);
+  void __fastcall FindMoveCandidateActionExecute(TObject *Sender);
 
 public:
   __fastcall TSynchronizeChecklistDialog(
@@ -154,6 +158,12 @@ protected:
   std::unique_ptr<Exception> FException;
   std::map<const TSynchronizeChecklist::TItem *, TListItem *> FChecklistToListViewMap;
   int FDirectories;
+  int FMoveCandidatesValidForSort;
+  typedef std::vector<const TSynchronizeChecklist::TItem *> TChecklistItems;
+  typedef std::map<UnicodeString, TChecklistItems> TMoveCandidatesFileNameMap;
+  TMoveCandidatesFileNameMap FMoveCandidatesFileName;
+  typedef std::map<__int64, TChecklistItems> TMoveCandidatesSizeMap;
+  TMoveCandidatesSizeMap FMoveCandidatesSize;
 
   void __fastcall UpdateControls();
   void __fastcall UpdateCaption();
@@ -192,6 +202,9 @@ protected:
   void __fastcall StatusBarHintShow(TCMHintShow & HintShow);
   DYNAMIC void __fastcall KeyDown(Word & Key, TShiftState Shift);
   void CalculateSize(bool All);
+  TIEListViewColProperties * GetColProperties();
+  TSynchronizeChecklist::TAction GetOppositeMoveAction(TSynchronizeChecklist::TAction Action1);
+  bool IsTransferNewAction(TSynchronizeChecklist::TAction Action);
 };
 //----------------------------------------------------------------------------
 #endif

+ 0 - 6
source/packages/filemng/CustomDirView.pas

@@ -318,7 +318,6 @@ type
     function ItemIsFile(Item: TListItem): Boolean; virtual; abstract;
     function ItemMatchesFilter(Item: TListItem; const Filter: TFileFilter): Boolean; virtual; abstract;
     function ItemOverlayIndexes(Item: TListItem): Word; virtual;
-    function IsItemVisible(Item: TListItem): Boolean;
     procedure LimitHistorySize;
     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
     procedure PathChanged; virtual;
@@ -1664,11 +1663,6 @@ begin
     OnGetOverlay(Self, Item, Result);
 end;
 
-function TCustomDirView.IsItemVisible(Item: TListItem): Boolean;
-begin
-  Result := (ListView_IsItemVisible(Handle, Item.Index) <> 0);
-end;
-
 procedure TCustomDirView.WMKeyDown(var Message: TWMKeyDown);
 begin
   if DoubleBuffered and (Message.CharCode in [VK_PRIOR, VK_NEXT]) and

+ 6 - 0
source/packages/my/NortonLikeListView.pas

@@ -92,6 +92,7 @@ type
       States: TItemStates): TListItem;
     procedure MakeProgressVisible(Item: TListItem);
     procedure FocusItem(Item: TListItem);
+    function IsItemVisible(Item: TListItem): Boolean;
 
     property ColProperties: TCustomListViewColProperties read FColProperties write FColProperties stored False;
 
@@ -1113,6 +1114,11 @@ begin
   Item.MakeVisible(False);
 end;
 
+function TCustomNortonLikeListView.IsItemVisible(Item: TListItem): Boolean;
+begin
+  Result := (ListView_IsItemVisible(Handle, Item.Index) <> 0);
+end;
+
 procedure TCustomNortonLikeListView.ChangeScale(M, D: Integer; isDpiChange: Boolean);
 begin
   if M <> D then

+ 16 - 6
source/windows/VCLCommon.cpp

@@ -48,6 +48,20 @@ static int __fastcall GetColumnTextWidth(TListView * ListView, int ColumnPadding
     ListView->Canvas->TextExtent(Text).Width;
 }
 //---------------------------------------------------------------------------
+void DisableRedraw(TWinControl * Control)
+{
+  SendMessage(Control->Handle, WM_SETREDRAW, false, 0);
+}
+//---------------------------------------------------------------------------
+void EnableRedraw(TWinControl * Control)
+{
+  SendMessage(Control->Handle, WM_SETREDRAW, true, 0);
+  // As recommended by documentation for WM_SETREDRAW.
+  // Without this, session list on Import dialog stops working after the list is reloaded while visible
+  // (i.e. when chaing import source)
+  RedrawWindow(Control->Handle, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
+}
+//---------------------------------------------------------------------------
 void __fastcall AutoSizeListColumnsWidth(TListView * ListView, int ColumnToShrinkIndex)
 {
   // Preallocate handle to precreate columns, otherwise our changes may get
@@ -57,7 +71,7 @@ void __fastcall AutoSizeListColumnsWidth(TListView * ListView, int ColumnToShrin
   // what may cause a flicker of the currently focused window title.
   ListView->HandleNeeded();
 
-  SendMessage(ListView->Handle, WM_SETREDRAW, false, 0);
+  DisableRedraw(ListView);
 
   try
   {
@@ -198,11 +212,7 @@ void __fastcall AutoSizeListColumnsWidth(TListView * ListView, int ColumnToShrin
   }
   __finally
   {
-    SendMessage(ListView->Handle, WM_SETREDRAW, true, 0);
-    // As recommended by documentation for WM_SETREDRAW.
-    // Without this, session list on Import dialog stops working after the list is reloaded while visible
-    // (i.e. when chaing import source)
-    RedrawWindow(ListView->Handle, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
+    EnableRedraw(ListView);
   }
 }
 //---------------------------------------------------------------------------

+ 2 - 0
source/windows/VCLCommon.h

@@ -102,5 +102,7 @@ void GiveTBItemPriority(Tb2item::TTBCustomItem * Item);
 void DeleteChildren(TWinControl * Control);
 void AutoSizeLabel(TLabel * Label);
 void AutoSizeLabel(TStaticText * Label);
+void DisableRedraw(TWinControl * Control);
+void EnableRedraw(TWinControl * Control);
 //---------------------------------------------------------------------------
 #endif  // VCLCommonH