Răsfoiți Sursa

Bug 1893: Allowing opening both local and remote tabs using "new tab" tab

https://winscp.net/tracker/1893

Source commit: c6b495cd3f9021f2d9d8fcddb0587644dd2ade4c
Martin Prikryl 4 ani în urmă
părinte
comite
fb28d236fb

+ 93 - 46
source/components/ThemePageControl.cpp

@@ -34,7 +34,7 @@ __fastcall TThemeTabSheet::TThemeTabSheet(TComponent * Owner) :
   TTabSheet(Owner)
 {
   FShadowed = false;
-  FShowCloseButton = false;
+  FButton = ttbNone;
 }
 //----------------------------------------------------------------------------------------------------------
 void __fastcall TThemeTabSheet::Invalidate()
@@ -59,11 +59,11 @@ void __fastcall TThemeTabSheet::SetShadowed(bool Value)
   }
 }
 //----------------------------------------------------------------------------------------------------------
-void __fastcall TThemeTabSheet::SetShowCloseButton(bool Value)
+void __fastcall TThemeTabSheet::SetButton(TThemeTabSheetButtons Value)
 {
-  if (ShowCloseButton != Value)
+  if (Button != Value)
   {
-    FShowCloseButton = Value;
+    FButton = Value;
     Invalidate();
   }
 }
@@ -73,7 +73,8 @@ __fastcall TThemePageControl::TThemePageControl(TComponent * Owner) :
   TPageControl(Owner)
 {
   FOldTabIndex = -1;
-  FHotCloseButton = -1;
+  FHotTabButton = -1;
+  FClickedButton = -1;
 }
 //----------------------------------------------------------------------------------------------------------
 int __fastcall TThemePageControl::GetTabsHeight()
@@ -142,10 +143,10 @@ void __fastcall TThemePageControl::PaintWindow(HDC DC)
   }
 }
 //----------------------------------------------------------------------------------------------------------
-bool __fastcall TThemePageControl::HasTabCloseButton(int Index)
+TThemeTabSheetButtons __fastcall TThemePageControl::GetTabButton(int Index)
 {
   TThemeTabSheet * ThemeTabSheet = dynamic_cast<TThemeTabSheet *>(Pages[Index]);
-  return (UseThemes() && (ThemeTabSheet != NULL)) ? ThemeTabSheet->ShowCloseButton : false;
+  return (UseThemes() && (ThemeTabSheet != NULL)) ? ThemeTabSheet->Button : ttbNone;
 }
 //----------------------------------------------------------------------------------------------------------
 void __fastcall TThemePageControl::DrawThemesXpTab(HDC DC, int Tab)
@@ -224,7 +225,7 @@ void __fastcall TThemePageControl::ItemTabRect(int Item, TRect & Rect)
 {
   if (Item == TabIndex)
   {
-    // Countered in CloseButtonRect
+    // Countered in TabButtonRect
     Rect.Inflate(2, 2);
     Rect.Bottom--;
   }
@@ -268,7 +269,7 @@ void __fastcall TThemePageControl::ItemTextRect(int Item, TRect & Rect)
   OffsetRect(&Rect, 0, ((Item == TabIndex) ? 0 : -2));
 }
 //----------------------------------------------------------------------------------------------------------
-void __fastcall TThemePageControl::DrawCross(HDC DC, int Width, COLORREF Color, const TRect & Rect)
+void TThemePageControl::DrawCross(HDC DC, int Width, COLORREF Color, const TRect & Rect)
 {
   HPEN Pen = CreatePen(PS_SOLID, Width, Color);
   HPEN OldPen = static_cast<HPEN>(SelectObject(DC, Pen));
@@ -283,6 +284,22 @@ void __fastcall TThemePageControl::DrawCross(HDC DC, int Width, COLORREF Color,
   DeleteObject(Pen);
 }
 //----------------------------------------------------------------------------------------------------------
+void TThemePageControl::DrawDropDown(HDC DC, int Radius, int X, int Y, COLORREF Color, int Grow)
+{
+  // Optimized for even-sized Rect (100% scaling), may need adjustments for even-sized to correctly center
+  TPoint Points[] = {
+    Point(X - Radius - 1 - Grow, Y), Point(X + Radius + Grow, Y),
+    Point(X, Y + Radius + Grow), Point(X - 1, Y + Radius + Grow)
+  };
+  HBRUSH Brush = CreateSolidBrush(Color);
+  HPEN Pen = CreatePen(PS_SOLID, 1, Color);
+  HGDIOBJ OldBrush = SelectObject(DC, Brush);
+  HGDIOBJ OldPen = SelectObject(DC, Pen);
+  Polygon(DC, Points, LENOF(Points));
+  SelectObject(DC, OldPen);
+  SelectObject(DC, OldBrush);
+}
+//----------------------------------------------------------------------------------------------------------
 // draw tab item context: possible icon and text
 void __fastcall TThemePageControl::DrawTabItem(
   HDC DC, int Item, TRect TabRect, TRect Rect, bool Selected, bool Shadowed)
@@ -323,12 +340,13 @@ void __fastcall TThemePageControl::DrawTabItem(
     DrawText(DC, Buf, -1, &Rect, DT_NOPREFIX | DT_CENTER);
     delete[] Buf;
 
-    if (HasTabCloseButton(Item))
+    TThemeTabSheetButtons Button = GetTabButton(Item);
+    if (Button != ttbNone)
     {
-      Rect = CloseButtonRect(Item);
+      Rect = TabButtonRect(Item);
       Rect.Offset(-TabRect.Left, -TabRect.Top);
 
-      if (FHotCloseButton == Item)
+      if (IsHotButton(Item))
       {
         HBRUSH Brush = CreateSolidBrush(GetSelectedBodyColor());
         FillRect(DC, &Rect, Brush);
@@ -341,20 +359,32 @@ void __fastcall TThemePageControl::DrawTabItem(
         DeleteObject(Pen);
       }
 
-      int CrossPadding = GetCrossPadding();
-
       COLORREF BackColor = GetPixel(DC, Rect.Left + (Rect.Width() / 2), Rect.Top + (Rect.Height() / 2));
-      COLORREF CrossColor = ColorToRGB(Font->Color);
-      #define BlendValue(FN) (((4 * static_cast<int>(FN(BackColor))) + static_cast<int>(FN(CrossColor))) / 5)
+      COLORREF ShapeColor = ColorToRGB(Font->Color);
+      #define BlendValue(FN) (((4 * static_cast<int>(FN(BackColor))) + static_cast<int>(FN(ShapeColor))) / 5)
       COLORREF BlendColor = RGB(BlendValue(GetRValue), BlendValue(GetGValue), BlendValue(GetBValue));
       #undef BlendValue
 
-      TRect CrossRect(Rect);
-      CrossRect.Inflate(-CrossPadding, -CrossPadding);
+      if (Button == ttbClose)
+      {
+        int CrossPadding = GetCrossPadding();
+
+        TRect CrossRect(Rect);
+        CrossRect.Inflate(-CrossPadding, -CrossPadding);
 
-      int CrossWidth = ScaleByTextHeight(this, 1);
-      DrawCross(DC, CrossWidth + 1, BlendColor, CrossRect);
-      DrawCross(DC, CrossWidth, CrossColor, CrossRect);
+        int CrossWidth = ScaleByTextHeight(this, 1);
+        DrawCross(DC, CrossWidth + 1, BlendColor, CrossRect);
+        DrawCross(DC, CrossWidth, ShapeColor, CrossRect);
+      }
+      else if (DebugAlwaysTrue(Button == ttbDropDown))
+      {
+        // See TTBXOfficeXPTheme.PaintDropDownArrow
+        int Radius = ScaleByTextHeight(this, 2);
+        int X = ((Rect.Left + Rect.Right)) / 2;
+        int Y = ((Rect.Top + Rect.Bottom) / 2) - (Radius * 2 / 3);
+        DrawDropDown(DC, Radius, X, Y, BlendColor, 1);
+        DrawDropDown(DC, Radius, X, Y, ShapeColor, 0);
+      }
     }
 
     SelectObject(DC, OldFont);
@@ -363,7 +393,7 @@ void __fastcall TThemePageControl::DrawTabItem(
   SetBkMode(DC, OldMode);
 }
 //----------------------------------------------------------------------------------------------------------
-int __fastcall TThemePageControl::CloseButtonSize()
+int __fastcall TThemePageControl::TabButtonSize()
 {
   return ScaleByTextHeight(this, 16);
 }
@@ -373,59 +403,68 @@ int __fastcall TThemePageControl::GetCrossPadding()
   return ScaleByTextHeight(this, 4);
 }
 //----------------------------------------------------------------------------------------------------------
-TRect __fastcall TThemePageControl::CloseButtonRect(int Index)
+TRect __fastcall TThemePageControl::TabButtonRect(int Index)
 {
   TRect Rect = TabRect(Index);
   ItemTabRect(Index, Rect);
   ItemContentsRect(Index, Rect);
   ItemTextRect(Index, Rect);
 
-  int ACloseButtonSize = CloseButtonSize();
+  int ATabButtonSize = TabButtonSize();
   int CrossPadding = GetCrossPadding();
 
   TEXTMETRIC TextMetric;
   Canvas->Font = Font;
   GetTextMetrics(Canvas->Handle, &TextMetric);
 
-  Rect.Top += TextMetric.tmAscent - ACloseButtonSize + CrossPadding;
-  Rect.Left = Rect.Right - ACloseButtonSize - ScaleByTextHeight(this, 1);
+  Rect.Top += TextMetric.tmAscent - ATabButtonSize + CrossPadding;
+  Rect.Left = Rect.Right - ATabButtonSize - ScaleByTextHeight(this, 1);
   if (Index == TabIndex)
   {
     // To counter Inflate(2, 2) in ItemTabRect
     Rect.Left -= 2;
   }
-  Rect.Right = Rect.Left + ACloseButtonSize;
-  Rect.Bottom = Rect.Top + ACloseButtonSize;
+  Rect.Right = Rect.Left + ATabButtonSize;
+  Rect.Bottom = Rect.Top + ATabButtonSize;
   return Rect;
 }
 //----------------------------------------------------------------------------------------------------------
-void __fastcall TThemePageControl::SetHotCloseButton(int Index)
+bool TThemePageControl::IsHotButton(int Index)
+{
+  // This was an attempt to allow tracking close buttons, even while drop down button menu is popped,
+  // but MouseMove does not trigger then.
+  return (Index == FClickedButton) || (Index == FHotTabButton);
+}
+//----------------------------------------------------------------------------------------------------------
+void TThemePageControl::UpdateHotButton(int & Ref, int Index)
 {
-  if (Index != FHotCloseButton)
+  if (Ref != Index)
   {
-    if (FHotCloseButton >= 0)
+    bool WasHot = (Index >= 0) && IsHotButton(Index);
+    int Prev = Ref;
+    Ref = Index;
+    if ((Prev >= 0) && !IsHotButton(Prev))
     {
-      InvalidateTab(FHotCloseButton);
+      InvalidateTab(Prev);
     }
-    FHotCloseButton = Index;
-    if (FHotCloseButton >= 0)
+    if ((Index >= 0) && !WasHot)
     {
-      InvalidateTab(FHotCloseButton);
+      InvalidateTab(Index);
     }
   }
 }
 //----------------------------------------------------------------------------------------------------------
 void __fastcall TThemePageControl::MouseMove(TShiftState /*Shift*/, int X, int Y)
 {
-  SetHotCloseButton(IndexOfCloseButtonAt(X, Y));
+  UpdateHotButton(FHotTabButton, IndexOfTabButtonAt(X, Y));
 }
 //----------------------------------------------------------------------------------------------------------
-int __fastcall TThemePageControl::IndexOfCloseButtonAt(int X, int Y)
+int __fastcall TThemePageControl::IndexOfTabButtonAt(int X, int Y)
 {
   int Result = IndexOfTabAt(X, Y);
   if ((Result < 0) ||
-      !HasTabCloseButton(Result) ||
-      !CloseButtonRect(Result).Contains(TPoint(X, Y)))
+      !GetTabButton(Result) ||
+      !TabButtonRect(Result).Contains(TPoint(X, Y)))
   {
     Result = -1;
   }
@@ -477,14 +516,14 @@ void __fastcall TThemePageControl::Change()
   TPageControl::Change();
 }
 //----------------------------------------------------------------------------------------------------------
-UnicodeString __fastcall TThemePageControl::FormatCaptionWithCloseButton(const UnicodeString & Caption)
+UnicodeString __fastcall TThemePageControl::FormatCaptionWithTabButton(const UnicodeString & Caption)
 {
   UnicodeString Result = Caption;
   if (UseThemes())
   {
     int OrigWidth = Canvas->TextWidth(Caption);
-    int CloseButtonWidth = CloseButtonSize();
-    while (Canvas->TextWidth(Result) < OrigWidth + CloseButtonWidth)
+    int TabButtonWidth = TabButtonSize();
+    while (Canvas->TextWidth(Result) < OrigWidth + TabButtonWidth)
     {
       Result += L" ";
     }
@@ -494,13 +533,21 @@ UnicodeString __fastcall TThemePageControl::FormatCaptionWithCloseButton(const U
 //---------------------------------------------------------------------------
 void __fastcall TThemePageControl::WMLButtonDown(TWMLButtonDown & Message)
 {
-  int Index = IndexOfCloseButtonAt(Message.XPos, Message.YPos);
+  int Index = IndexOfTabButtonAt(Message.XPos, Message.YPos);
   if (Index >= 0)
   {
     Message.Result = 1;
-    if (FOnCloseButtonClick != NULL)
+    if (FOnTabButtonClick != NULL)
     {
-      FOnCloseButtonClick(this, Index);
+      UpdateHotButton(FClickedButton, Index);
+      try
+      {
+        FOnTabButtonClick(this, Index);
+      }
+      __finally
+      {
+        UpdateHotButton(FClickedButton, -1);
+      }
     }
   }
   else
@@ -514,7 +561,7 @@ void __fastcall TThemePageControl::Dispatch(void * Message)
   TMessage * M = reinterpret_cast<TMessage*>(Message);
   if (M->Msg == CM_MOUSELEAVE)
   {
-    SetHotCloseButton(-1);
+    UpdateHotButton(FHotTabButton, -1);
     TPageControl::Dispatch(Message);
   }
   else if (M->Msg == WM_LBUTTONDOWN)

+ 19 - 14
source/components/ThemePageControl.h

@@ -4,37 +4,40 @@
 //---------------------------------------------------------------------------
 #include <ComCtrls.hpp>
 //---------------------------------------------------------------------------
+enum TThemeTabSheetButtons { ttbNone, ttbClose, ttbDropDown };
+//---------------------------------------------------------------------------
 class TThemeTabSheet : public TTabSheet
 {
 public:
   __fastcall TThemeTabSheet(TComponent * Owner);
 
   __property bool Shadowed = { read = FShadowed, write = SetShadowed };
-  __property bool ShowCloseButton = { read = FShowCloseButton, write = SetShowCloseButton };
+  __property TThemeTabSheetButtons Button = { read = FButton, write = SetButton };
 
 private:
   void __fastcall SetShadowed(bool Value);
-  void __fastcall SetShowCloseButton(bool Value);
+  void __fastcall SetButton(TThemeTabSheetButtons Value);
   void __fastcall Invalidate();
 
   bool FShadowed;
-  bool FShowCloseButton;
+  TThemeTabSheetButtons FButton;
 };
 //---------------------------------------------------------------------------
-typedef void __fastcall (__closure *TPageControlCloseButtonClick)(TPageControl * Sender, int Index);
+typedef void __fastcall (__closure *TPageControlTabButtonClick)(TPageControl * Sender, int Index);
 //---------------------------------------------------------------------------
 class TThemePageControl : public TPageControl
 {
 friend class TThemeTabSheet;
 
 __published:
-  __property TPageControlCloseButtonClick OnCloseButtonClick = { read = FOnCloseButtonClick, write = FOnCloseButtonClick };
+  __property TPageControlTabButtonClick OnTabButtonClick = { read = FOnTabButtonClick, write = FOnTabButtonClick };
 
 public:
   __fastcall TThemePageControl(TComponent * Owner);
 
   int __fastcall GetTabsHeight();
-  UnicodeString __fastcall FormatCaptionWithCloseButton(const UnicodeString & Caption);
+  UnicodeString __fastcall FormatCaptionWithTabButton(const UnicodeString & Caption);
+  TRect __fastcall TabButtonRect(int Index);
 
 protected:
   virtual void __fastcall PaintWindow(HDC DC);
@@ -52,22 +55,24 @@ private:
   void __fastcall DrawTabItem(HDC DC, int Item, TRect TabRect, TRect Rect, bool Selected, bool Shadowed);
   void __fastcall DrawThemesPart(HDC DC, int PartId, int StateId, LPCWSTR PartNameID, LPRECT Rect);
   void __fastcall InvalidateTab(int Index);
-  int __fastcall CloseButtonSize();
+  int __fastcall TabButtonSize();
   int __fastcall GetCrossPadding();
-  TRect __fastcall CloseButtonRect(int Index);
-  int __fastcall IndexOfCloseButtonAt(int X, int Y);
+  int __fastcall IndexOfTabButtonAt(int X, int Y);
   void __fastcall ItemContentsRect(int Item, TRect & Rect);
   bool __fastcall HasItemImage(int Item);
   void __fastcall ItemTextRect(int Item, TRect & Rect);
   void __fastcall ItemTabRect(int Item, TRect & Rect);
-  bool __fastcall HasTabCloseButton(int Index);
-  void __fastcall SetHotCloseButton(int Index);
-  void __fastcall DrawCross(HDC DC, int Width, COLORREF Color, const TRect & Rect);
+  TThemeTabSheetButtons __fastcall GetTabButton(int Index);
+  void UpdateHotButton(int & Ref, int Index);
+  void DrawCross(HDC DC, int Width, COLORREF Color, const TRect & Rect);
+  void DrawDropDown(HDC DC, int Radius, int X, int Y, COLORREF Color, int Grow);
   void __fastcall WMLButtonDown(TWMLButtonDown & Message);
+  bool IsHotButton(int Index);
 
   int FOldTabIndex;
-  int FHotCloseButton;
-  TPageControlCloseButtonClick FOnCloseButtonClick;
+  int FHotTabButton;
+  int FClickedButton;
+  TPageControlTabButtonClick FOnTabButtonClick;
 };
 //---------------------------------------------------------------------------
 #endif

+ 54 - 17
source/forms/CustomScpExplorer.cpp

@@ -248,8 +248,8 @@ __fastcall TCustomScpExplorerForm::TCustomScpExplorerForm(TComponent* Owner):
   TransferList->OnChange = TransferListChange;
   TransferList->OnDrawItem = TransferListDrawItem;
 
-  SetSubmenu(dynamic_cast<TTBXCustomItem *>(static_cast<TObject *>(GetComponent(fcColorMenu))));
-  SetSubmenu(NonVisualDataModule->ColorMenuItem);
+  SetSubmenu(dynamic_cast<TTBXCustomItem *>(static_cast<TObject *>(GetComponent(fcColorMenu))), true);
+  SetSubmenu(NonVisualDataModule->ColorMenuItem, true);
 
   UseDesktopFont(SessionsPageControl);
   UpdateSessionsPageControlHeight();
@@ -1700,6 +1700,11 @@ bool TCustomScpExplorerForm::IsLocalBrowserMode()
   return false;
 }
 //---------------------------------------------------------------------------
+bool TCustomScpExplorerForm::SupportsLocalBrowser()
+{
+  return false;
+}
+//---------------------------------------------------------------------------
 bool TCustomScpExplorerForm::IsSideLocalBrowser(TOperationSide)
 {
   return false;
@@ -6853,7 +6858,7 @@ void __fastcall TCustomScpExplorerForm::SessionListChanged()
       {
         TabSheet->Tag = 0; // not really needed
         TabSheet->Shadowed = false;
-        TabSheet->ShowCloseButton = false;
+        TabSheet->Button = SupportsLocalBrowser() ? ttbDropDown : ttbNone;
         // 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();
       }
@@ -6871,11 +6876,19 @@ void __fastcall TCustomScpExplorerForm::UpdateNewTabTab()
 {
   TTabSheet * TabSheet = SessionsPageControl->Pages[SessionsPageControl->PageCount - 1];
 
+  UnicodeString TabCaption;
+  if (WinConfiguration->SelectiveToolbarText)
+  {
+    TabCaption = StripHotkey(StripTrailingPunctuation(NonVisualDataModule->NewTabAction->Caption));
+  }
+  TThemeTabSheet * ThemeTabSheet = dynamic_cast<TThemeTabSheet *>(TabSheet);
+  // When starting, we can get here with the design-time unthemed tab
+  if ((ThemeTabSheet != NULL) && (ThemeTabSheet->Button != ttbNone))
+  {
+    TabCaption = SessionsPageControl->FormatCaptionWithTabButton(TabCaption);
+  }
+  TabSheet->Caption = TabCaption;
 
-  TabSheet->Caption =
-    WinConfiguration->SelectiveToolbarText ?
-      StripHotkey(StripTrailingPunctuation(NonVisualDataModule->NewSessionAction->Caption)) :
-      UnicodeString();
   TabSheet->ImageIndex = GetNewTabTabImageIndex(osCurrent);
 }
 //---------------------------------------------------------------------------
@@ -6914,10 +6927,10 @@ void __fastcall TCustomScpExplorerForm::UpdateSessionTab(TTabSheet * TabSheet)
       if (DebugAlwaysTrue(ThemeTabSheet != NULL))
       {
         ThemeTabSheet->Shadowed = !ASession->Active && !ASession->LocalBrowser;
-        ThemeTabSheet->ShowCloseButton = CanCloseSession(ASession);
-        if (ThemeTabSheet->ShowCloseButton)
+        ThemeTabSheet->Button = CanCloseSession(ASession) ? ttbClose : ttbNone;
+        if (ThemeTabSheet->Button != ttbNone)
         {
-          TabCaption = SessionsPageControl->FormatCaptionWithCloseButton(TabCaption);
+          TabCaption = SessionsPageControl->FormatCaptionWithTabButton(TabCaption);
         }
       }
 
@@ -10398,8 +10411,9 @@ void __fastcall TCustomScpExplorerForm::SessionsPageControlContextPopup(TObject
   int Index = SessionsPageControl->IndexOfTabAt(MousePos.X, MousePos.Y);
   if (Index >= 0)
   {
+    TPopupMenu * PopupMenu = NULL;
+
     TManagedTerminal * Session = GetSessionTabSession(SessionsPageControl->Pages[Index]);
-    // no context menu for "New session tab"
     if (Session != NULL)
     {
       SessionsPageControl->ActivePageIndex = Index;
@@ -10413,12 +10427,23 @@ void __fastcall TCustomScpExplorerForm::SessionsPageControlContextPopup(TObject
         // to avoid menu to popup somewhere within SessionTabSwitched above,
         // while connecting yet not-connected session and hence
         // allowing an access to commands over not-completelly connected session
-        TPoint Point = SessionsPageControl->ClientToScreen(MousePos);
-        TPopupMenu * PopupMenu = Session->LocalBrowser ? NonVisualDataModule->LocalBrowserPopup : NonVisualDataModule->SessionsPopup;
-        PopupMenu->PopupComponent = SessionsPageControl;
-        PopupMenu->Popup(Point.x, Point.y);
+        PopupMenu = Session->LocalBrowser ? NonVisualDataModule->LocalBrowserPopup : NonVisualDataModule->SessionsPopup;
       }
     }
+    else
+    {
+      if (SupportsLocalBrowser())
+      {
+        PopupMenu = NonVisualDataModule->NewTabPopup;
+      }
+    }
+
+    if (PopupMenu != NULL)
+    {
+      TPoint Point = SessionsPageControl->ClientToScreen(MousePos);
+      PopupMenu->PopupComponent = SessionsPageControl;
+      PopupMenu->Popup(Point.x, Point.y);
+    }
   }
   Handled = true;
 }
@@ -10703,9 +10728,21 @@ void __fastcall TCustomScpExplorerForm::CloseSessionTab(int Index)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TCustomScpExplorerForm::SessionsPageControlCloseButtonClick(TPageControl * /*Sender*/, int Index)
+void __fastcall TCustomScpExplorerForm::SessionsPageControlTabButtonClick(TPageControl *, int Index)
 {
-  CloseSessionTab(Index);
+  TManagedTerminal * Session = GetSessionTabSession(SessionsPageControl->Pages[Index]);
+  if (Session == NULL)
+  {
+    TRect ButtonRect = SessionsPageControl->TabButtonRect(Index);
+    ButtonRect = TRect(SessionsPageControl->ClientToScreen(ButtonRect.TopLeft()), SessionsPageControl->ClientToScreen(ButtonRect.BottomRight()));
+    //TPoint P = TPoint(ButtonRect.Left, ButtonRect.Bottom);
+    //P = SessionsPageControl->ClientToScreen(P);
+    MenuPopup(NonVisualDataModule->NewTabPopup, ButtonRect, SessionsPageControl);
+  }
+  else
+  {
+    CloseSessionTab(Index);
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::CopyFilesToClipboard(TOperationSide Side, bool OnFocused)

+ 1 - 1
source/forms/CustomScpExplorer.dfm

@@ -390,7 +390,7 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
     OnDragDrop = SessionsPageControlDragDrop
     OnDragOver = SessionsPageControlDragOver
     OnMouseDown = SessionsPageControlMouseDown
-    OnCloseButtonClick = SessionsPageControlCloseButtonClick
+    OnTabButtonClick = SessionsPageControlTabButtonClick
     object TabSheet1: TTabSheet
       Caption = 'TabSheet1'
     end

+ 2 - 1
source/forms/CustomScpExplorer.h

@@ -198,7 +198,7 @@ __published:
   void __fastcall DirViewBusy(TObject *Sender, int Busy, bool & Allow);
   void __fastcall SessionsPageControlContextPopup(TObject *Sender, TPoint &MousePos, bool &Handled);
   void __fastcall DockContextPopup(TObject *Sender, TPoint &MousePos, bool &Handled);
-  void __fastcall SessionsPageControlCloseButtonClick(TPageControl *Sender, int Index);
+  void __fastcall SessionsPageControlTabButtonClick(TPageControl *Sender, int Index);
   void __fastcall DirViewGetItemColor(
     TObject * Sender, UnicodeString FileName, bool Directory, __int64 Size, TDateTime Modification, TColor & Color);
   void __fastcall DirViewExit(TObject *Sender);
@@ -864,6 +864,7 @@ public:
   void __fastcall ReplaceTerminal(TManagedTerminal * value);
   virtual void __fastcall BrowseFile();
   void __fastcall CloseApp();
+  virtual bool SupportsLocalBrowser();
   virtual bool IsSideLocalBrowser(TOperationSide Side);
   virtual UnicodeString GetLocalBrowserSessionTitle(TManagedTerminal * Session);
   virtual int GetNewTabActionImageIndex();

+ 1 - 1
source/forms/Editor.cpp

@@ -685,7 +685,7 @@ __fastcall TEditorForm::TEditorForm(TComponent* Owner)
   FClosePending = false;
   FReloading = false;
   FInternalEditorEncodingOverride = -1;
-  SetSubmenu(ColorItem);
+  SetSubmenu(ColorItem, true);
 
   InitCodePage();
   SelectScaledImageList(EditorImages);

+ 3 - 0
source/forms/NonVisual.cpp

@@ -893,6 +893,9 @@ void __fastcall TNonVisualDataModule::ExplorerShortcuts()
   CloseApplicationAction2->ShortCut = ShortCut(VK_F4, ALT);
 
   CloneShortcuts();
+
+  SessionsNewTabItem->DropdownCombo = false;
+  SetSubmenu(SessionsNewTabItem, false);
 }
 //---------------------------------------------------------------------------
 void __fastcall TNonVisualDataModule::CommanderShortcuts()

+ 45 - 4
source/forms/NonVisual.dfm

@@ -3231,8 +3231,20 @@ object NonVisualDataModule: TNonVisualDataModule
     end
     object TBXSeparatorItem34: TTBXSeparatorItem
     end
-    object TBXItem123: TTBXItem
-      Action = NewSessionAction
+    object SessionsNewTabItem: TTBXSubmenuItem
+      Action = NewTabAction
+      DropdownCombo = True
+      object TBXItem104: TTBXItem
+        Action = NewRemoteTabAction
+      end
+      object TBXItem105: TTBXItem
+        Action = NewLocalTabAction
+      end
+      object TBXSeparatorItem18: TTBXSeparatorItem
+      end
+      object TBXItem106: TTBXItem
+        Action = DefaultToNewRemoteTabAction
+      end
     end
     object TBXSubmenuItem23: TTBXSubmenuItem
       Action = SavedSessionsAction2
@@ -3335,8 +3347,20 @@ object NonVisualDataModule: TNonVisualDataModule
     end
     object TBXSeparatorItem21: TTBXSeparatorItem
     end
-    object TBXItem109: TTBXItem
-      Action = NewSessionAction
+    object TBXSubmenuItem12: TTBXSubmenuItem
+      Action = NewTabAction
+      DropdownCombo = True
+      object TBXItem107: TTBXItem
+        Action = NewRemoteTabAction
+      end
+      object TBXItem108: TTBXItem
+        Action = NewLocalTabAction
+      end
+      object TBXSeparatorItem19: TTBXSeparatorItem
+      end
+      object TBXItem111: TTBXItem
+        Action = DefaultToNewRemoteTabAction
+      end
     end
     object TBXSubmenuItem11: TTBXSubmenuItem
       Action = SavedSessionsAction2
@@ -3348,4 +3372,21 @@ object NonVisualDataModule: TNonVisualDataModule
       Action = SessionsTabsAction2
     end
   end
+  object NewTabPopup: TTBXPopupMenu
+    Images = GlyphsModule.ExplorerImages
+    Options = [tboShowHint]
+    Left = 256
+    Top = 176
+    object NewRemoteTabItem: TTBXItem
+      Action = NewRemoteTabAction
+    end
+    object NewLocalTabItem: TTBXItem
+      Action = NewLocalTabAction
+    end
+    object TBXSeparatorItem67: TTBXSeparatorItem
+    end
+    object TBXItem232: TTBXItem
+      Action = DefaultToNewRemoteTabAction
+    end
+  end
 end

+ 15 - 1
source/forms/NonVisual.h

@@ -665,7 +665,6 @@ __published:    // IDE-managed Components
   TTBXPopupMenu *LocalBrowserPopup;
   TTBXItem *TBXItem62;
   TTBXSeparatorItem *TBXSeparatorItem21;
-  TTBXItem *TBXItem109;
   TTBXSubmenuItem *TBXSubmenuItem11;
   TTBXSeparatorItem *TBXSeparatorItem22;
   TTBXItem *TBXItem110;
@@ -675,6 +674,21 @@ __published:    // IDE-managed Components
   TAction *NewLocalTabAction;
   TAction *NewRemoteTabAction;
   TAction *DefaultToNewRemoteTabAction;
+  TTBXPopupMenu *NewTabPopup;
+  TTBXItem *NewRemoteTabItem;
+  TTBXItem *NewLocalTabItem;
+  TTBXSeparatorItem *TBXSeparatorItem67;
+  TTBXItem *TBXItem232;
+  TTBXSubmenuItem *SessionsNewTabItem;
+  TTBXItem *TBXItem104;
+  TTBXItem *TBXItem105;
+  TTBXSeparatorItem *TBXSeparatorItem18;
+  TTBXItem *TBXItem106;
+  TTBXSubmenuItem *TBXSubmenuItem12;
+  TTBXItem *TBXItem107;
+  TTBXItem *TBXItem108;
+  TTBXSeparatorItem *TBXSeparatorItem19;
+  TTBXItem *TBXItem111;
   void __fastcall ExplorerActionsUpdate(TBasicAction *Action, bool &Handled);
   void __fastcall ExplorerActionsExecute(TBasicAction *Action, bool &Handled);
   void __fastcall SessionIdleTimerTimer(TObject *Sender);

+ 5 - 0
source/forms/ScpCommander.cpp

@@ -501,6 +501,11 @@ TCustomDriveView * __fastcall TScpCommanderForm::DriveView(TOperationSide Side)
   }
 }
 //---------------------------------------------------------------------------
+bool TScpCommanderForm::SupportsLocalBrowser()
+{
+  return true;
+}
+//---------------------------------------------------------------------------
 bool TScpCommanderForm::IsSideLocalBrowser(TOperationSide Side)
 {
   return (GetSide(Side) == osLocal) || IsLocalBrowserMode();

+ 1 - 0
source/forms/ScpCommander.h

@@ -660,6 +660,7 @@ public:
   virtual void __fastcall CopyFilesToClipboard(TOperationSide Side, bool OnFocused);
   virtual void __fastcall PasteFromClipBoard();
   virtual void __fastcall BrowseFile();
+  virtual bool SupportsLocalBrowser();
   virtual bool IsSideLocalBrowser(TOperationSide Side);
   virtual bool IsLocalBrowserMode();
   virtual void LocalLocalCopy(

+ 1 - 0
source/packages/tbx/TBXOfficeXPTheme.pas

@@ -597,6 +597,7 @@ var
   X, Y: Integer;
   Two: Integer;
 begin
+  // See TThemePageControl::DrawTabItem with ttbDropDown
   with ARect, Canvas do
   begin
     X := (Left + Right) div 2;

+ 9 - 2
source/windows/GUITools.cpp

@@ -688,7 +688,7 @@ void __fastcall RegenerateSessionColorsImageList(TCustomImageList * ImageList, i
   DebugAssert(SessionColors->ColorMap == ColorMap);
 }
 //---------------------------------------------------------------------------
-void __fastcall SetSubmenu(TTBXCustomItem * Item)
+void __fastcall SetSubmenu(TTBXCustomItem * Item, bool Enable)
 {
   class TTBXPublicItem : public TTBXCustomItem
   {
@@ -698,7 +698,14 @@ void __fastcall SetSubmenu(TTBXCustomItem * Item)
   TTBXPublicItem * PublicItem = reinterpret_cast<TTBXPublicItem *>(Item);
   DebugAssert(PublicItem != NULL);
   // See TTBItemViewer.IsPtInButtonPart (called from TTBItemViewer.MouseDown)
-  PublicItem->ItemStyle = PublicItem->ItemStyle << tbisSubmenu;
+  if (Enable)
+  {
+    PublicItem->ItemStyle = PublicItem->ItemStyle << tbisSubmenu;
+  }
+  else
+  {
+    PublicItem->ItemStyle = PublicItem->ItemStyle >> tbisSubmenu;
+  }
 }
 //---------------------------------------------------------------------------
 bool __fastcall IsEligibleForApplyingTabs(

+ 1 - 1
source/windows/GUITools.h

@@ -32,7 +32,7 @@ UnicodeString __fastcall UniqTempDir(const UnicodeString BaseDir,
 bool __fastcall DeleteDirectory(const UnicodeString DirName);
 int __fastcall GetSessionColorImage(TCustomImageList * ImageList, TColor Color, int MaskIndex);
 void __fastcall RegenerateSessionColorsImageList(TCustomImageList * ImageList, int MaskIndex);
-void __fastcall SetSubmenu(TTBXCustomItem * Item);
+void __fastcall SetSubmenu(TTBXCustomItem * Item, bool Enable);
 typedef int __fastcall (*TCalculateWidth)(UnicodeString Text, void * Arg);
 void __fastcall ApplyTabs(
   UnicodeString & Text, wchar_t Padding,