Explorar o código

Bug 1423: Tab titles are shortened to fit window width as needed

https://winscp.net/tracker/1423

Tabs show tooltips with full session name and paths

+ Reserving space to tab button is encapsulated to the TThemeTabSheet class

Source commit: 30b49eb47b29d5f8cae1fcbdd4abc9670026fa44
Martin Prikryl %!s(int64=4) %!d(string=hai) anos
pai
achega
45ef69246d

+ 1 - 1
source/DScpComp.cbproj

@@ -50,7 +50,7 @@
 		<ILINK_GenerateImportLibrary>true</ILINK_GenerateImportLibrary>
 		<ILINK_GenerateImportLibrary>true</ILINK_GenerateImportLibrary>
 		<ILINK_GenerateLibFile>true</ILINK_GenerateLibFile>
 		<ILINK_GenerateLibFile>true</ILINK_GenerateLibFile>
 		<ILINK_LibraryPath>components\;$(ILINK_LibraryPath)</ILINK_LibraryPath>
 		<ILINK_LibraryPath>components\;$(ILINK_LibraryPath)</ILINK_LibraryPath>
-		<IncludePath>components\;core;packages\filemng;packages\dragndrop;packages\my;packages\tb2k;packages\tbx;$(BDS)\include;$(BDS)\include\windows\vcl;$(IncludePath)</IncludePath>
+		<IncludePath>components\;components;core;windows;packages\filemng;packages\dragndrop;packages\my;packages\tb2k;packages\tbx;$(BDS)\include;$(BDS)\include\windows\vcl;$(IncludePath)</IncludePath>
 		<IntermediateOutputDir>$(INTERM_PATH)\$(Platform)\$(Config)</IntermediateOutputDir>
 		<IntermediateOutputDir>$(INTERM_PATH)\$(Platform)\$(Config)</IntermediateOutputDir>
 		<LinkPackageStatics>rtl.lib;vcl.lib;vclx.lib</LinkPackageStatics>
 		<LinkPackageStatics>rtl.lib;vcl.lib;vclx.lib</LinkPackageStatics>
 		<Multithreaded>true</Multithreaded>
 		<Multithreaded>true</Multithreaded>

+ 171 - 16
source/components/ThemePageControl.cpp

@@ -7,6 +7,8 @@
 #include <memory>
 #include <memory>
 #include <PasTools.hpp>
 #include <PasTools.hpp>
 #include <TBXOfficeXPTheme.hpp>
 #include <TBXOfficeXPTheme.hpp>
+#include <StrUtils.hpp>
+#include <CustomWinConfiguration.h>
 #include "ThemePageControl.h"
 #include "ThemePageControl.h"
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 #pragma package(smart_init)
@@ -35,11 +37,17 @@ __fastcall TThemeTabSheet::TThemeTabSheet(TComponent * Owner) :
 {
 {
   FShadowed = false;
   FShadowed = false;
   FButton = ttbNone;
   FButton = ttbNone;
+  FCaptionTruncation = tttNone;
+}
+//----------------------------------------------------------------------------------------------------------
+TThemePageControl * TThemeTabSheet::GetParentPageControl()
+{
+  return DebugNotNull(dynamic_cast<TThemePageControl *>(Parent));
 }
 }
 //----------------------------------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------------------------------
 void __fastcall TThemeTabSheet::Invalidate()
 void __fastcall TThemeTabSheet::Invalidate()
 {
 {
-  TThemePageControl * ThemePageControl = dynamic_cast<TThemePageControl *>(Parent);
+  TThemePageControl * ThemePageControl = GetParentPageControl();
   if (DebugAlwaysTrue(ThemePageControl != NULL))
   if (DebugAlwaysTrue(ThemePageControl != NULL))
   {
   {
     ThemePageControl->InvalidateTab(TabIndex);
     ThemePageControl->InvalidateTab(TabIndex);
@@ -68,6 +76,78 @@ void __fastcall TThemeTabSheet::SetButton(TThemeTabSheetButtons Value)
   }
   }
 }
 }
 //----------------------------------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------------------------------
+void TThemeTabSheet::SetBaseCaption(const UnicodeString & value)
+{
+  if (FBaseCaption != value)
+  {
+    FBaseCaption = value;
+    UpdateCaption();
+  }
+}
+//----------------------------------------------------------------------------------------------------------
+UnicodeString TThemeTabSheet::TruncatedCaption()
+{
+  UnicodeString Result = FBaseCaption;
+  TThemePageControl * ParentPageControl = GetParentPageControl();
+  if (ParentPageControl->FSessionTabShrink > 0)
+  {
+    if (FCaptionTruncation == tttNone)
+    {
+      // noop
+    }
+    else if (FCaptionTruncation == tttEllipsis)
+    {
+      if (ParentPageControl->FSessionTabShrink == 1)
+      {
+        Result = Result.SubString(1, 1);
+      }
+      else if (ParentPageControl->FSessionTabShrink < Result.Length())
+      {
+        Result = Result.SubString(1, ParentPageControl->FSessionTabShrink - 1) + Ellipsis;
+      }
+    }
+    else if (DebugAlwaysTrue(FCaptionTruncation == tttNoText))
+    {
+      Result = EmptyStr;
+    }
+  }
+  return Result;
+}
+//----------------------------------------------------------------------------------------------------------
+void TThemeTabSheet::UpdateCaption()
+{
+  UnicodeString ACaption = TruncatedCaption();
+
+  TThemePageControl * ParentPageControl = GetParentPageControl();
+
+  if (UseThemes())
+  {
+    int OrigWidth = ParentPageControl->Canvas->TextWidth(ACaption);
+    int TabButtonWidth = ParentPageControl->TabButtonSize();
+    while (ParentPageControl->Canvas->TextWidth(ACaption) < OrigWidth + TabButtonWidth)
+    {
+      ACaption += L" ";
+    }
+  }
+  Caption = ACaption;
+}
+//----------------------------------------------------------------------------------------------------------
+UnicodeString TThemeTabSheet::GetBaseCaption()
+{
+  DebugAssert(StartsStr(FBaseCaption, Caption));
+  DebugAssert(RightStr(Caption, Caption.Length() - FBaseCaption.Length()).Trim().Length() == 0);
+  return FBaseCaption;
+}
+//----------------------------------------------------------------------------------------------------------
+void TThemeTabSheet::SetCaptionTruncation(TThemeTabCaptionTruncation Value)
+{
+  if (FCaptionTruncation != Value)
+  {
+    FCaptionTruncation = Value;
+    UpdateCaption();
+  }
+}
+//----------------------------------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------------------------------
 __fastcall TThemePageControl::TThemePageControl(TComponent * Owner) :
 __fastcall TThemePageControl::TThemePageControl(TComponent * Owner) :
   TPageControl(Owner)
   TPageControl(Owner)
@@ -75,6 +155,9 @@ __fastcall TThemePageControl::TThemePageControl(TComponent * Owner) :
   FOldTabIndex = -1;
   FOldTabIndex = -1;
   FHotTabButton = -1;
   FHotTabButton = -1;
   FClickedButton = -1;
   FClickedButton = -1;
+  FSessionTabShrink = 0;
+  FOnTabButtonClick = NULL;
+  FOnTabHint = NULL;
 }
 }
 //----------------------------------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------------------------------
 int __fastcall TThemePageControl::GetTabsHeight()
 int __fastcall TThemePageControl::GetTabsHeight()
@@ -516,21 +599,6 @@ void __fastcall TThemePageControl::Change()
 
 
   TPageControl::Change();
   TPageControl::Change();
 }
 }
-//----------------------------------------------------------------------------------------------------------
-UnicodeString __fastcall TThemePageControl::FormatCaptionWithTabButton(const UnicodeString & Caption)
-{
-  UnicodeString Result = Caption;
-  if (UseThemes())
-  {
-    int OrigWidth = Canvas->TextWidth(Caption);
-    int TabButtonWidth = TabButtonSize();
-    while (Canvas->TextWidth(Result) < OrigWidth + TabButtonWidth)
-    {
-      Result += L" ";
-    }
-  }
-  return Result;
-}
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 void __fastcall TThemePageControl::WMLButtonDown(TWMLButtonDown & Message)
 void __fastcall TThemePageControl::WMLButtonDown(TWMLButtonDown & Message)
 {
 {
@@ -557,6 +625,17 @@ void __fastcall TThemePageControl::WMLButtonDown(TWMLButtonDown & Message)
   }
   }
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
+void TThemePageControl::CMHintShow(TCMHintShow & HintShow)
+{
+  TPageControl::Dispatch(&HintShow);
+  if (OnTabHint != NULL)
+  {
+    int Tab = IndexOfTabAt(HintShow.HintInfo->CursorPos.x, HintShow.HintInfo->CursorPos.y);
+    OnTabHint(this, Tab, HintShow.HintInfo->HintStr);
+    HintShow.HintInfo->CursorRect = TabRect(Tab);
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TThemePageControl::Dispatch(void * Message)
 void __fastcall TThemePageControl::Dispatch(void * Message)
 {
 {
   TMessage * M = reinterpret_cast<TMessage*>(Message);
   TMessage * M = reinterpret_cast<TMessage*>(Message);
@@ -569,6 +648,14 @@ void __fastcall TThemePageControl::Dispatch(void * Message)
   {
   {
     WMLButtonDown(*reinterpret_cast<TWMLButtonDown *>(M));
     WMLButtonDown(*reinterpret_cast<TWMLButtonDown *>(M));
   }
   }
+  else if (M->Msg == WM_WANTS_SCREEN_TIPS)
+  {
+    M->Result = 1;
+  }
+  else if (M->Msg == CM_HINTSHOW)
+  {
+    CMHintShow(*reinterpret_cast<TCMHintShow *>(M));
+  }
   else
   else
   {
   {
     TPageControl::Dispatch(Message);
     TPageControl::Dispatch(Message);
@@ -591,6 +678,74 @@ TThemeTabSheet * TThemePageControl::GetActivePage()
   return Result;
   return Result;
 }
 }
 //----------------------------------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------------------------------
+int TThemePageControl::TotalTabsWidth()
+{
+  TRect FirstTabRect = TabRect(0);
+  TRect LastTabRect = TabRect(PageCount - 1);
+  return -FirstTabRect.Left + LastTabRect.Right;
+}
+//----------------------------------------------------------------------------------------------------------
+void TThemePageControl::UpdateTabsCaptionTruncation()
+{
+  FSessionTabShrink = 0;
+  for (int Index = 0; Index < PageCount; Index++)
+  {
+    Pages[Index]->UpdateCaption();
+  }
+
+  int TabsWidth = TotalTabsWidth();
+  int MaxWidth = ClientWidth - ScaleByTextHeight(this, 8); // arbitrary margin to avoid left/right buttons flicker
+  if (TabsWidth > MaxWidth)
+  {
+    int NeedWidth = (TabsWidth - MaxWidth);
+    int MaxLen = 0;
+    int CaptionsWidth = 0;
+    for (int Index = 0; Index < PageCount; Index++)
+    {
+      UnicodeString TabCaption = Pages[Index]->BaseCaption;
+      MaxLen = std::max(MaxLen, TabCaption.Length());
+      CaptionsWidth += Canvas->TextWidth(TabCaption);
+    }
+
+    bool Repeat;
+    do
+    {
+      int NewShrink;
+      if (FSessionTabShrink == 0)
+      {
+        NewShrink = MaxLen; // remove only new tab caption
+      }
+      else
+      {
+        NewShrink = FSessionTabShrink - 1;
+      }
+
+      if (NewShrink < 1)
+      {
+        Repeat = false;
+      }
+      else
+      {
+        FSessionTabShrink = NewShrink;
+        int NewCaptionsWidth = 0;
+        for (int Index = 0; Index < PageCount; Index++)
+        {
+          UnicodeString TabCaption = Pages[Index]->TruncatedCaption();
+          NewCaptionsWidth += Canvas->TextWidth(TabCaption);
+        }
+        int GainedWidth = (CaptionsWidth - NewCaptionsWidth);
+        Repeat = (GainedWidth < NeedWidth);
+      }
+    }
+    while (Repeat);
+
+    for (int Index = 0; Index < PageCount; Index++)
+    {
+      Pages[Index]->UpdateCaption();
+    }
+  }
+}
+//----------------------------------------------------------------------------------------------------------
 #ifdef _DEBUG
 #ifdef _DEBUG
 void __fastcall TThemePageControl::RequestAlign()
 void __fastcall TThemePageControl::RequestAlign()
 {
 {

+ 21 - 1
source/components/ThemePageControl.h

@@ -5,25 +5,40 @@
 #include <ComCtrls.hpp>
 #include <ComCtrls.hpp>
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 enum TThemeTabSheetButtons { ttbNone, ttbClose, ttbDropDown };
 enum TThemeTabSheetButtons { ttbNone, ttbClose, ttbDropDown };
+enum TThemeTabCaptionTruncation { tttNone, tttEllipsis, tttNoText };
+class TThemePageControl;
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 class TThemeTabSheet : public TTabSheet
 class TThemeTabSheet : public TTabSheet
 {
 {
+friend class TThemePageControl;
+
 public:
 public:
   __fastcall TThemeTabSheet(TComponent * Owner);
   __fastcall TThemeTabSheet(TComponent * Owner);
 
 
+  __property UnicodeString BaseCaption = { read = GetBaseCaption, write = SetBaseCaption };
   __property bool Shadowed = { read = FShadowed, write = SetShadowed };
   __property bool Shadowed = { read = FShadowed, write = SetShadowed };
   __property TThemeTabSheetButtons Button = { read = FButton, write = SetButton };
   __property TThemeTabSheetButtons Button = { read = FButton, write = SetButton };
+  __property TThemeTabCaptionTruncation CaptionTruncation = { read = FCaptionTruncation, write = SetCaptionTruncation };
 
 
 private:
 private:
   void __fastcall SetShadowed(bool Value);
   void __fastcall SetShadowed(bool Value);
   void __fastcall SetButton(TThemeTabSheetButtons Value);
   void __fastcall SetButton(TThemeTabSheetButtons Value);
   void __fastcall Invalidate();
   void __fastcall Invalidate();
+  void SetBaseCaption(const UnicodeString & value);
+  UnicodeString GetBaseCaption();
+  TThemePageControl * GetParentPageControl();
+  void SetCaptionTruncation(TThemeTabCaptionTruncation Value);
+  void UpdateCaption();
+  UnicodeString TruncatedCaption();
 
 
   bool FShadowed;
   bool FShadowed;
   TThemeTabSheetButtons FButton;
   TThemeTabSheetButtons FButton;
+  UnicodeString FBaseCaption;
+  TThemeTabCaptionTruncation FCaptionTruncation;
 };
 };
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 typedef void __fastcall (__closure *TPageControlTabButtonClick)(TPageControl * Sender, int Index);
 typedef void __fastcall (__closure *TPageControlTabButtonClick)(TPageControl * Sender, int Index);
+typedef void __fastcall (__closure *TPageControlTabHint)(TPageControl * Sender, int Index, UnicodeString & Hint);
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 class TThemePageControl : public TPageControl
 class TThemePageControl : public TPageControl
 {
 {
@@ -31,6 +46,7 @@ friend class TThemeTabSheet;
 
 
 __published:
 __published:
   __property TPageControlTabButtonClick OnTabButtonClick = { read = FOnTabButtonClick, write = FOnTabButtonClick };
   __property TPageControlTabButtonClick OnTabButtonClick = { read = FOnTabButtonClick, write = FOnTabButtonClick };
+  __property TPageControlTabHint OnTabHint = { read = FOnTabHint, write = FOnTabHint };
 
 
 public:
 public:
   __fastcall TThemePageControl(TComponent * Owner);
   __fastcall TThemePageControl(TComponent * Owner);
@@ -39,8 +55,9 @@ public:
   __property TThemeTabSheet * ActivePage = { read = GetActivePage };
   __property TThemeTabSheet * ActivePage = { read = GetActivePage };
 
 
   int __fastcall GetTabsHeight();
   int __fastcall GetTabsHeight();
-  UnicodeString __fastcall FormatCaptionWithTabButton(const UnicodeString & Caption);
   TRect __fastcall TabButtonRect(int Index);
   TRect __fastcall TabButtonRect(int Index);
+  int TotalTabsWidth();
+  void UpdateTabsCaptionTruncation();
 
 
 protected:
 protected:
   virtual void __fastcall PaintWindow(HDC DC);
   virtual void __fastcall PaintWindow(HDC DC);
@@ -73,11 +90,14 @@ private:
   bool IsHotButton(int Index);
   bool IsHotButton(int Index);
   TThemeTabSheet * GetPage(int Index);
   TThemeTabSheet * GetPage(int Index);
   TThemeTabSheet * GetActivePage();
   TThemeTabSheet * GetActivePage();
+  void CMHintShow(TCMHintShow & Message);
 
 
   int FOldTabIndex;
   int FOldTabIndex;
   int FHotTabButton;
   int FHotTabButton;
   int FClickedButton;
   int FClickedButton;
   TPageControlTabButtonClick FOnTabButtonClick;
   TPageControlTabButtonClick FOnTabButtonClick;
+  TPageControlTabHint FOnTabHint;
+  int FSessionTabShrink;
 };
 };
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 #endif
 #endif

+ 92 - 24
source/forms/CustomScpExplorer.cpp

@@ -4079,8 +4079,6 @@ bool __fastcall TCustomScpExplorerForm::RemoteTransferDialog(TManagedTerminal *&
   DebugAssert(Terminal != NULL);
   DebugAssert(Terminal != NULL);
   DebugAssert(Terminal == TTerminalManager::Instance()->ActiveTerminal);
   DebugAssert(Terminal == TTerminalManager::Instance()->ActiveTerminal);
   DebugAssert(!IsLocalBrowserMode());
   DebugAssert(!IsLocalBrowserMode());
-  // update Terminal->StateData->RemoteDirectory
-  UpdateSession(Terminal);
 
 
   if (Session == NULL)
   if (Session == NULL)
   {
   {
@@ -4105,7 +4103,7 @@ bool __fastcall TCustomScpExplorerForm::RemoteTransferDialog(TManagedTerminal *&
   }
   }
   else
   else
   {
   {
-    Target = Session->StateData->RemoteDirectory;
+    Target = GetSessionPath(Session, osRemote);
   }
   }
 
 
   Target = UnixIncludeTrailingBackslash(Target);
   Target = UnixIncludeTrailingBackslash(Target);
@@ -4139,7 +4137,7 @@ bool __fastcall TCustomScpExplorerForm::RemoteTransferDialog(TManagedTerminal *&
         if (IsActiveTerminal(ASession) && DebugAlwaysTrue(!ASession->LocalBrowser))
         if (IsActiveTerminal(ASession) && DebugAlwaysTrue(!ASession->LocalBrowser))
         {
         {
           Sessions->AddObject(SessionList->Strings[Index], ASession);
           Sessions->AddObject(SessionList->Strings[Index], ASession);
-          Directories->Add(ASession->StateData->RemoteDirectory);
+          Directories->Add(GetSessionPath(ASession, osRemote));
         }
         }
       }
       }
 
 
@@ -6813,24 +6811,25 @@ void __fastcall TCustomScpExplorerForm::NeedSession(bool Startup)
   }
   }
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-void __fastcall TCustomScpExplorerForm::SessionListChanged()
+void __fastcall TCustomScpExplorerForm::SessionListChanged(bool ForceTruncationUpdate)
 {
 {
   TTerminalManager * Manager = TTerminalManager::Instance();
   TTerminalManager * Manager = TTerminalManager::Instance();
   DebugAssert(!Manager->Updating);
   DebugAssert(!Manager->Updating);
-  TStrings * SessionList = Manager->SessionList;
+  TAutoFlag UpdatingSessionTabsFlag(FUpdatingSessionTabs);
   int ActiveSessionIndex = Manager->ActiveSessionIndex;
   int ActiveSessionIndex = Manager->ActiveSessionIndex;
 
 
-  Configuration->Usage->SetMax(L"MaxOpenedSessions", SessionList->Count);
+  Configuration->Usage->SetMax(L"MaxOpenedSessions", Manager->Count);
 
 
   SendMessage(SessionsPageControl->Handle, WM_SETREDRAW, 0, 0);
   SendMessage(SessionsPageControl->Handle, WM_SETREDRAW, 0, 0);
   try
   try
   {
   {
+    int PrevTabsWidth = SessionsPageControl->TotalTabsWidth();
     while (SessionsPageControl->PageCount > Manager->Count + 1)
     while (SessionsPageControl->PageCount > Manager->Count + 1)
     {
     {
       delete SessionsPageControl->Pages[SessionsPageControl->PageCount - 1];
       delete SessionsPageControl->Pages[SessionsPageControl->PageCount - 1];
     }
     }
 
 
-    for (int Index = 0; Index <= SessionList->Count; Index++)
+    for (int Index = 0; Index <= Manager->Count; Index++)
     {
     {
       TThemeTabSheet * TabSheet;
       TThemeTabSheet * TabSheet;
       if (Index >= SessionsPageControl->PageCount)
       if (Index >= SessionsPageControl->PageCount)
@@ -6843,11 +6842,12 @@ void __fastcall TCustomScpExplorerForm::SessionListChanged()
         TabSheet = SessionsPageControl->Pages[Index];
         TabSheet = SessionsPageControl->Pages[Index];
       }
       }
 
 
-      bool IsSessionTab = (Index < SessionList->Count);
+      bool IsSessionTab = (Index < Manager->Count);
       if (IsSessionTab)
       if (IsSessionTab)
       {
       {
-        TTerminal * Terminal = dynamic_cast<TTerminal *>(SessionList->Objects[Index]);
+        TTerminal * Terminal = Manager->Sessions[Index];
         TabSheet->Tag = reinterpret_cast<int>(Terminal);
         TabSheet->Tag = reinterpret_cast<int>(Terminal);
+        TabSheet->CaptionTruncation = tttEllipsis;
 
 
         UpdateSessionTab(TabSheet);
         UpdateSessionTab(TabSheet);
       }
       }
@@ -6856,10 +6856,17 @@ void __fastcall TCustomScpExplorerForm::SessionListChanged()
         TabSheet->Tag = 0; // not really needed
         TabSheet->Tag = 0; // not really needed
         TabSheet->Shadowed = false;
         TabSheet->Shadowed = false;
         TabSheet->Button = SupportsLocalBrowser() ? ttbDropDown : ttbNone;
         TabSheet->Button = SupportsLocalBrowser() ? ttbDropDown : ttbNone;
+        TabSheet->CaptionTruncation = tttNoText;
         // We know that we are at the last page, otherwise we could not call this (it assumes that new session tab is the last one)
         // We know that we are at the last page, otherwise we could not call this (it assumes that new session tab is the last one)
         UpdateNewTabTab();
         UpdateNewTabTab();
       }
       }
     }
     }
+
+    // Prevent flicker on the right side where scrolling buttons would be, unless really needed
+    if (ForceTruncationUpdate || (SessionsPageControl->TotalTabsWidth() != PrevTabsWidth))
+    {
+      SessionsPageControl->UpdateTabsCaptionTruncation();
+    }
   }
   }
   __finally
   __finally
   {
   {
@@ -6869,6 +6876,11 @@ void __fastcall TCustomScpExplorerForm::SessionListChanged()
   SessionsPageControl->ActivePageIndex = ActiveSessionIndex;
   SessionsPageControl->ActivePageIndex = ActiveSessionIndex;
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
+UnicodeString TCustomScpExplorerForm::GetNewTabTabCaption()
+{
+  return StripHotkey(StripTrailingPunctuation(NonVisualDataModule->NewTabAction->Caption));
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::UpdateNewTabTab()
 void __fastcall TCustomScpExplorerForm::UpdateNewTabTab()
 {
 {
   TThemeTabSheet * TabSheet = SessionsPageControl->Pages[SessionsPageControl->PageCount - 1];
   TThemeTabSheet * TabSheet = SessionsPageControl->Pages[SessionsPageControl->PageCount - 1];
@@ -6876,14 +6888,9 @@ void __fastcall TCustomScpExplorerForm::UpdateNewTabTab()
   UnicodeString TabCaption;
   UnicodeString TabCaption;
   if (WinConfiguration->SelectiveToolbarText)
   if (WinConfiguration->SelectiveToolbarText)
   {
   {
-    TabCaption = StripHotkey(StripTrailingPunctuation(NonVisualDataModule->NewTabAction->Caption));
-  }
-  if (TabSheet->Button != ttbNone)
-  {
-    TabCaption = SessionsPageControl->FormatCaptionWithTabButton(TabCaption);
+    TabCaption = GetNewTabTabCaption();
   }
   }
-  TabSheet->Caption = TabCaption;
-
+  TabSheet->BaseCaption = TabCaption;
   TabSheet->ImageIndex = GetNewTabTabImageIndex(osCurrent);
   TabSheet->ImageIndex = GetNewTabTabImageIndex(osCurrent);
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
@@ -6916,16 +6923,10 @@ void TCustomScpExplorerForm::UpdateSessionTab(TThemeTabSheet * TabSheet)
       }
       }
 
 
       TTerminalManager * Manager = TTerminalManager::Instance();
       TTerminalManager * Manager = TTerminalManager::Instance();
-      UnicodeString TabCaption = Manager->GetSessionTitle(ASession, true);
 
 
       TabSheet->Shadowed = !ASession->Active && !ASession->LocalBrowser;
       TabSheet->Shadowed = !ASession->Active && !ASession->LocalBrowser;
       TabSheet->Button = CanCloseSession(ASession) ? ttbClose : ttbNone;
       TabSheet->Button = CanCloseSession(ASession) ? ttbClose : ttbNone;
-      if (TabSheet->Button != ttbNone)
-      {
-        TabCaption = SessionsPageControl->FormatCaptionWithTabButton(TabCaption);
-      }
-
-      TabSheet->Caption = TabCaption;
+      TabSheet->BaseCaption = Manager->GetSessionTitle(ASession, true);
     }
     }
   }
   }
 }
 }
@@ -11227,3 +11228,70 @@ void TCustomScpExplorerForm::LocalLocalCopyCommand(TFileOperation Operation, TOp
 {
 {
   LocalLocalCopy(Operation, Side, OnFocused, !WinConfiguration->ConfirmTransferring, false, Flags);
   LocalLocalCopy(Operation, Side, OnFocused, !WinConfiguration->ConfirmTransferring, false, Flags);
 }
 }
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::SessionsPageControlResize(TObject *)
+{
+  // Changing tab list triggers the OnResize.
+  if (!FUpdatingSessionTabs)
+  {
+    // UpdateTabsCaptionTruncation should be enough
+    SessionListChanged(true);
+  }
+}
+//---------------------------------------------------------------------------
+UnicodeString TCustomScpExplorerForm::GetSessionPath(TManagedTerminal * ASession, TOperationSide Side)
+{
+  if (ASession == ManagedSession)
+  {
+    UpdateSession(ASession);
+  }
+  TSessionData * Data = ASession->StateData;
+  UnicodeString Result;
+  if (Side == osLocal)
+  {
+    Result = Data->LocalDirectory;
+  }
+  else if (DebugAlwaysTrue(Side == osOther))
+  {
+    Result = ASession->LocalBrowser ? Data->OtherLocalDirectory : Data->RemoteDirectory;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+UnicodeString TCustomScpExplorerForm::GetTabHintDetails(TManagedTerminal * ASession)
+{
+  UnicodeString Result;
+  if (!ASession->Active)
+  {
+    Result = LoadStr(STATUS_NOT_CONNECTED2);
+  }
+  else
+  {
+    Result = GetSessionPath(ASession, osRemote);
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::SessionsPageControlTabHint(TPageControl *, int Index, UnicodeString & Hint)
+{
+  if (Index >= 0)
+  {
+    TThemeTabSheet * TabSheet = SessionsPageControl->Pages[Index];
+    TManagedTerminal * TabSession = GetSessionTabSession(TabSheet);
+    if (TabSession == NULL)
+    {
+      UnicodeString NewTabTabCaption = GetNewTabTabCaption();
+      if (NewTabTabCaption != TabSheet->Caption)
+      {
+        Hint = NewTabTabCaption;
+      }
+    }
+    else
+    {
+      TTerminalManager * Manager = TTerminalManager::Instance();
+      UnicodeString TabCaption = Manager->GetSessionTitle(TabSession, true);
+      Hint = TabCaption + L"|" + GetTabHintDetails(TabSession);
+    }
+  }
+}
+//---------------------------------------------------------------------------

+ 4 - 0
source/forms/CustomScpExplorer.dfm

@@ -383,6 +383,8 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
     Align = alTop
     Align = alTop
     DoubleBuffered = True
     DoubleBuffered = True
     ParentDoubleBuffered = False
     ParentDoubleBuffered = False
+    ParentShowHint = False
+    ShowHint = True
     TabOrder = 3
     TabOrder = 3
     TabStop = False
     TabStop = False
     OnChange = SessionsPageControlChange
     OnChange = SessionsPageControlChange
@@ -390,7 +392,9 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
     OnDragDrop = SessionsPageControlDragDrop
     OnDragDrop = SessionsPageControlDragDrop
     OnDragOver = SessionsPageControlDragOver
     OnDragOver = SessionsPageControlDragOver
     OnMouseDown = SessionsPageControlMouseDown
     OnMouseDown = SessionsPageControlMouseDown
+    OnResize = SessionsPageControlResize
     OnTabButtonClick = SessionsPageControlTabButtonClick
     OnTabButtonClick = SessionsPageControlTabButtonClick
+    OnTabHint = SessionsPageControlTabHint
     object TabSheet1: TThemeTabSheet
     object TabSheet1: TThemeTabSheet
       Caption = 'TabSheet1'
       Caption = 'TabSheet1'
     end
     end

+ 7 - 1
source/forms/CustomScpExplorer.h

@@ -216,6 +216,8 @@ __published:
   void __fastcall QueueFileListData(TObject *Sender, TListItem *Item);
   void __fastcall QueueFileListData(TObject *Sender, TListItem *Item);
   void __fastcall QueueFileListCustomDrawItem(TCustomListView *Sender, TListItem *Item, TCustomDrawState State, bool &DefaultDraw);
   void __fastcall QueueFileListCustomDrawItem(TCustomListView *Sender, TListItem *Item, TCustomDrawState State, bool &DefaultDraw);
   void __fastcall QueueFileListResize(TObject *Sender);
   void __fastcall QueueFileListResize(TObject *Sender);
+  void __fastcall SessionsPageControlResize(TObject *Sender);
+  void __fastcall SessionsPageControlTabHint(TPageControl *Sender, int Index, UnicodeString &Hint);
 
 
 private:
 private:
   TManagedTerminal * FManagedSession;
   TManagedTerminal * FManagedSession;
@@ -295,6 +297,7 @@ private:
   bool FInvalid;
   bool FInvalid;
   std::auto_ptr<TQueueFileList> FQueueFileList;
   std::auto_ptr<TQueueFileList> FQueueFileList;
   bool FStarted;
   bool FStarted;
+  bool FUpdatingSessionTabs;
 
 
   bool __fastcall GetEnableFocusedOperation(TOperationSide Side, int FilesOnly);
   bool __fastcall GetEnableFocusedOperation(TOperationSide Side, int FilesOnly);
   bool __fastcall GetEnableSelectedOperation(TOperationSide Side, int FilesOnly);
   bool __fastcall GetEnableSelectedOperation(TOperationSide Side, int FilesOnly);
@@ -628,6 +631,7 @@ protected:
   int __fastcall AddSessionColor(TColor Color);
   int __fastcall AddSessionColor(TColor Color);
   void UpdateSessionTab(TThemeTabSheet * TabSheet);
   void UpdateSessionTab(TThemeTabSheet * TabSheet);
   void __fastcall UpdateNewTabTab();
   void __fastcall UpdateNewTabTab();
+  UnicodeString GetNewTabTabCaption();
   void __fastcall AddFixedSessionImages();
   void __fastcall AddFixedSessionImages();
   int __fastcall AddFixedSessionImage(int GlyphsSourceIndex);
   int __fastcall AddFixedSessionImage(int GlyphsSourceIndex);
   TObjectList * __fastcall DoCollectWorkspace();
   TObjectList * __fastcall DoCollectWorkspace();
@@ -722,6 +726,8 @@ protected:
   bool GetDoNotShowCopyDialogDefault(bool DragDrop);
   bool GetDoNotShowCopyDialogDefault(bool DragDrop);
   void HandleDoNotShowCopyDialogAgain(bool DragDrop, bool DoNotShowAgain);
   void HandleDoNotShowCopyDialogAgain(bool DragDrop, bool DoNotShowAgain);
   void __fastcall UpdateDarkMode();
   void __fastcall UpdateDarkMode();
+  virtual UnicodeString GetTabHintDetails(TManagedTerminal * ASession);
+  UnicodeString GetSessionPath(TManagedTerminal * ASession, TOperationSide Side);
 
 
 public:
 public:
   virtual __fastcall ~TCustomScpExplorerForm();
   virtual __fastcall ~TCustomScpExplorerForm();
@@ -806,7 +812,7 @@ public:
   void __fastcall TerminalRemoved(TObject * Sender);
   void __fastcall TerminalRemoved(TObject * Sender);
   void __fastcall TerminalDisconnected();
   void __fastcall TerminalDisconnected();
   void __fastcall TerminalConnecting();
   void __fastcall TerminalConnecting();
-  void __fastcall SessionListChanged();
+  void __fastcall SessionListChanged(bool ForceTruncationUpdate = false);
   void __fastcall ApplicationTitleChanged();
   void __fastcall ApplicationTitleChanged();
   unsigned int __fastcall MoreMessageDialog(const UnicodeString Message,
   unsigned int __fastcall MoreMessageDialog(const UnicodeString Message,
     TStrings * MoreMessages, TQueryType Type, unsigned int Answers,
     TStrings * MoreMessages, TQueryType Type, unsigned int Answers,

+ 27 - 0
source/forms/ScpCommander.cpp

@@ -2818,6 +2818,7 @@ UnicodeString TScpCommanderForm::GetLocalBrowserSessionTitle(TManagedTerminal *
     UnicodeString Path1;
     UnicodeString Path1;
     UnicodeString Path2;
     UnicodeString Path2;
     TTerminalManager * Manager = TTerminalManager::Instance();
     TTerminalManager * Manager = TTerminalManager::Instance();
+    // might use GetSessionPath here
     if ((ASession == ManagedSession) &&
     if ((ASession == ManagedSession) &&
         // prevent tab title flicker, when switching to local-local tab, as the path changes in individual local panels
         // prevent tab title flicker, when switching to local-local tab, as the path changes in individual local panels
         !FSessionChanging)
         !FSessionChanging)
@@ -2852,3 +2853,29 @@ int TScpCommanderForm::GetNewTabTabImageIndex(TOperationSide Side)
   }
   }
   return TCustomScpExplorerForm::GetNewTabTabImageIndex(Side);
   return TCustomScpExplorerForm::GetNewTabTabImageIndex(Side);
 }
 }
+//---------------------------------------------------------------------------
+UnicodeString TScpCommanderForm::GetTabHintDetails(TManagedTerminal * ASession)
+{
+  UnicodeString Result;
+  if (!ASession->LocalBrowser && !ASession->Active)
+  {
+    Result = LoadStr(STATUS_NOT_CONNECTED2);
+  }
+  else
+  {
+    UnicodeString Local = GetSessionPath(ASession, osLocal);
+    UnicodeString Other = GetSessionPath(ASession, osOther);
+    // Contrary to Panel(), the IsRightToLeft() is not considered here,
+    // as I assume they expect the right panel first, as they read from the right.
+    UnicodeString Sep = L"\n";
+    if (!WinConfiguration->ScpCommander.SwappedPanels)
+    {
+      Result = Local + Sep + Other;
+    }
+    else
+    {
+      Result = Other + Sep + Local;
+    }
+  }
+  return Result;
+}

+ 1 - 0
source/forms/ScpCommander.h

@@ -635,6 +635,7 @@ protected:
   void __fastcall SetToolbar2ItemAction(TTBXItem * Item, TBasicAction * Action);
   void __fastcall SetToolbar2ItemAction(TTBXItem * Item, TBasicAction * Action);
   virtual void __fastcall NeedSession(bool Startup);
   virtual void __fastcall NeedSession(bool Startup);
   void RestoreSessionLocalDirView(TDirView * ALocalDirView, const UnicodeString & LocalDirectory);
   void RestoreSessionLocalDirView(TDirView * ALocalDirView, const UnicodeString & LocalDirectory);
+  virtual UnicodeString GetTabHintDetails(TManagedTerminal * ASession);
 
 
 public:
 public:
   __fastcall TScpCommanderForm(TComponent* Owner);
   __fastcall TScpCommanderForm(TComponent* Owner);