From c48fb168404e2a24abcbdeb33cfecb11f9111928 Mon Sep 17 00:00:00 2001 From: Martin Prikryl Date: Wed, 12 Sep 2018 14:02:51 +0000 Subject: [PATCH] Bug 1393: Close button on session tab + Non-active session can be closed, as well as disconnected sessions without a need to reconnect them https://winscp.net/tracker/1393 Source commit: b63ce2363155fa969d91e07f294cca9f1c5c2a98 --- source/DScpComp.cbproj | 7 +- source/RScpComp.cbproj | 2 +- source/components/ThemePageControl.cpp | 322 +++++++++++++++++++---- source/components/ThemePageControl.h | 30 ++- source/forms/CustomScpExplorer.cpp | 48 +++- source/forms/CustomScpExplorer.dfm | 1 + source/forms/CustomScpExplorer.h | 2 + source/packages/tbx/TBXOfficeXPTheme.pas | 9 +- 8 files changed, 362 insertions(+), 59 deletions(-) diff --git a/source/DScpComp.cbproj b/source/DScpComp.cbproj index 34dcdd95f..fa124eb4f 100644 --- a/source/DScpComp.cbproj +++ b/source/DScpComp.cbproj @@ -49,8 +49,8 @@ WinSCP components true true - components\;$(ILINK_LibraryPath) - components\;core;packages\filemng;packages\dragndrop;packages\my;$(BDS)\include;$(BDS)\include\windows\vcl;$(IncludePath) + components\;$(INTERM_PATH)\Win32\Debug;$(ILINK_LibraryPath) + components\;core;packages\filemng;packages\dragndrop;packages\my;packages\tb2k;packages\tbx;$(BDS)\include;$(BDS)\include\windows\vcl;$(IncludePath) $(INTERM_PATH)\$(Platform)\$(Config) rtl.lib;vcl.lib;vclx.lib true @@ -116,6 +116,9 @@ 20 1 + + 8 + 23 0 diff --git a/source/RScpComp.cbproj b/source/RScpComp.cbproj index 0ca26b8df..a621ed4d1 100644 --- a/source/RScpComp.cbproj +++ b/source/RScpComp.cbproj @@ -38,7 +38,7 @@ System;Xml;Data;Datasnap;Web;Soap;Vcl;$(DCC_Namespace) $(INTERM_PATH)\$(Platform)\$(Config) $(BDS)\lib;$(ILINK_LibraryPath) - components\;core;windows;packages\filemng;packages\dragndrop;packages\my;$(BDS)\include;$(BDS)\include\windows\vcl;$(IncludePath) + components\;core;windows;packages\filemng;packages\dragndrop;packages\my;packages\tb2k;packages\tbx;$(BDS)\include;$(BDS)\include\windows\vcl;$(IncludePath) $(INTERM_PATH)\$(Platform)\$(Config) None true diff --git a/source/components/ThemePageControl.cpp b/source/components/ThemePageControl.cpp index 67e4cb151..92c9d274a 100644 --- a/source/components/ThemePageControl.cpp +++ b/source/components/ThemePageControl.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "ThemePageControl.h" //--------------------------------------------------------------------------- #pragma package(smart_init) @@ -39,6 +41,20 @@ __fastcall TThemeTabSheet::TThemeTabSheet(TComponent * Owner) : TTabSheet(Owner) { FShadowed = false; + FShowCloseButton = false; +} +//---------------------------------------------------------------------------------------------------------- +void __fastcall TThemeTabSheet::Invalidate() +{ + TThemePageControl * ThemePageControl = dynamic_cast(Parent); + if (DebugAlwaysTrue(ThemePageControl != NULL)) + { + ThemePageControl->InvalidateTab(TabIndex); + } + else + { + Parent->Invalidate(); + } } //---------------------------------------------------------------------------------------------------------- void __fastcall TThemeTabSheet::SetShadowed(bool Value) @@ -46,16 +62,16 @@ void __fastcall TThemeTabSheet::SetShadowed(bool Value) if (Shadowed != Value) { FShadowed = Value; - - TThemePageControl * ThemePageControl = dynamic_cast(Parent); - if (DebugAlwaysTrue(ThemePageControl != NULL)) - { - ThemePageControl->InvalidateTab(TabIndex); - } - else - { - Parent->Invalidate(); - } + Invalidate(); + } +} +//---------------------------------------------------------------------------------------------------------- +void __fastcall TThemeTabSheet::SetShowCloseButton(bool Value) +{ + if (ShowCloseButton != Value) + { + FShowCloseButton = Value; + Invalidate(); } } //---------------------------------------------------------------------------------------------------------- @@ -64,6 +80,7 @@ __fastcall TThemePageControl::TThemePageControl(TComponent * Owner) : TPageControl(Owner) { FOldTabIndex = -1; + FHotCloseButton = -1; } //---------------------------------------------------------------------------------------------------------- int __fastcall TThemePageControl::GetTabsHeight() @@ -116,32 +133,48 @@ void __fastcall TThemePageControl::PaintWindow(HDC DC) // 2nd paint the inactive tabs - TPoint Point = ScreenToClient(Mouse->CursorPos); - int HotIndex = IndexOfTabAt(Point.X, Point.Y); - int SelectedIndex = TabIndex; + int SelectedIndex = TabIndex; // optimization for (int Tab = 0; Tab < PageCount; Tab++) { if (Tab != SelectedIndex) { - TThemeTabSheet * ThemeTabSheet = dynamic_cast(Pages[Tab]); - bool Shadowed = (ThemeTabSheet != NULL) ? ThemeTabSheet->Shadowed : false; - TRect Rect = TabRect(Tab); - int State = (Tab == HotIndex ? TIS_HOT : (Shadowed ? TIS_DISABLED : TIS_NORMAL)); - DrawThemesXpTabItem(DC, Tab, Rect, false, State); + DrawThemesXpTab(DC, Tab); } } if (SelectedIndex >= 0) { - // 3rd paint the active selected tab - TRect Rect = TabRect(SelectedIndex); - Rect.Inflate(2, 2); - Rect.Bottom--; - DrawThemesXpTabItem(DC, SelectedIndex, Rect, false, TIS_SELECTED); + DrawThemesXpTab(DC, TabIndex); } } //---------------------------------------------------------------------------------------------------------- +bool __fastcall TThemePageControl::HasTabCloseButton(int Index) +{ + TThemeTabSheet * ThemeTabSheet = dynamic_cast(Pages[Index]); + return (ThemeTabSheet != NULL) ? ThemeTabSheet->ShowCloseButton : false; +} +//---------------------------------------------------------------------------------------------------------- +void __fastcall TThemePageControl::DrawThemesXpTab(HDC DC, int Tab) +{ + TThemeTabSheet * ThemeTabSheet = dynamic_cast(Pages[Tab]); + bool Shadowed = (ThemeTabSheet != NULL) ? ThemeTabSheet->Shadowed : false; + TRect Rect = TabRect(Tab); + ItemTabRect(Tab, Rect); + int State; + if (Tab != TabIndex) + { + TPoint Point = ScreenToClient(Mouse->CursorPos); + int HotIndex = IndexOfTabAt(Point.X, Point.Y); + State = (Tab == HotIndex ? TIS_HOT : (Shadowed ? TIS_DISABLED : TIS_NORMAL)); + } + else + { + State = TIS_SELECTED; + } + DrawThemesXpTabItem(DC, Tab, Rect, false, State); +} +//---------------------------------------------------------------------------------------------------------- // This function draws Themes Tab control parts: a) Tab-Body and b) Tab-tabs void __fastcall TThemePageControl::DrawThemesXpTabItem(HDC DC, int Item, const TRect & Rect, bool Body, int State) @@ -154,6 +187,7 @@ void __fastcall TThemePageControl::DrawThemesXpTabItem(HDC DC, int Item, HBITMAP BitmapOld = (HBITMAP)SelectObject(DCMem, BitmapMem); TRect RectMem(0, 0, Size.Width, Size.Height); + TRect RectItemMem(RectMem); if (!Body && (State == TIS_SELECTED)) { RectMem.Bottom++; @@ -183,12 +217,7 @@ void __fastcall TThemePageControl::DrawThemesXpTabItem(HDC DC, int Item, if (!Body && (Item >= 0)) { - if ((State == TIS_SELECTED)) - { - RectMem.Bottom--; - } - - DrawTabItem(DCMem, Item, RectMem, (State == TIS_SELECTED), (State == TIS_DISABLED)); + DrawTabItem(DCMem, Item, Rect, RectItemMem, (State == TIS_SELECTED), (State == TIS_DISABLED)); } // Blit image to the screen @@ -198,25 +227,78 @@ void __fastcall TThemePageControl::DrawThemesXpTabItem(HDC DC, int Item, DeleteDC(DCMem); } //---------------------------------------------------------------------------------------------------------- -// draw tab item context: possible icon and text -void __fastcall TThemePageControl::DrawTabItem(HDC DC, int Item, TRect Rect, - bool Selected, bool Shadowed) +void __fastcall TThemePageControl::ItemTabRect(int Item, TRect & Rect) { + if (Item == TabIndex) + { + // Countered in CloseButtonRect + Rect.Inflate(2, 2); + Rect.Bottom--; + } +} +//---------------------------------------------------------------------------------------------------------- +void __fastcall TThemePageControl::ItemContentsRect(int Item, TRect & Rect) +{ + bool Selected = (Item == TabIndex); + + Rect.Left += 6; + Rect.Top += 2; + if (Selected) { - Rect.Bottom -= 1; + Rect.Bottom -= 2; + Rect.Top += 1; } else { Rect.Bottom += 2; + Rect.Top += 3; } - - Rect.Left += 6; - Rect.Top += 2 + (Selected ? 1 : 3); +} +//---------------------------------------------------------------------------------------------------------- +bool __fastcall TThemePageControl::HasItemImage(int Item) +{ + return (Images != NULL) && (Pages[Item]->ImageIndex >= 0); +} +//---------------------------------------------------------------------------------------------------------- +void __fastcall TThemePageControl::ItemTextRect(int Item, TRect & Rect) +{ + if (HasItemImage(Item)) + { + Rect.Left += Images->Width + 3; + } + else + { + Rect.Left -= 2; + } + Rect.Right -= 3; + OffsetRect(&Rect, 0, ((Item == TabIndex) ? 0 : -2)); +} +//---------------------------------------------------------------------------------------------------------- +void __fastcall TThemePageControl::DrawCross(HDC DC, int Width, COLORREF Color, const TRect & Rect) +{ + HPEN Pen = CreatePen(PS_SOLID, Width, Color); + HPEN OldPen = static_cast(SelectObject(DC, Pen)); + // To-and-back - to make both ends look the same + MoveToEx(DC, Rect.Left, Rect.Bottom - 1, NULL); + LineTo(DC, Rect.Right - 1, Rect.Top); + LineTo(DC, Rect.Left, Rect.Bottom - 1); + MoveToEx(DC, Rect.Left, Rect.Top, NULL); + LineTo(DC, Rect.Right - 1, Rect.Bottom - 1); + LineTo(DC, Rect.Left, Rect.Top); + SelectObject(DC, OldPen); + DeleteObject(Pen); +} +//---------------------------------------------------------------------------------------------------------- +// draw tab item context: possible icon and text +void __fastcall TThemePageControl::DrawTabItem( + HDC DC, int Item, TRect TabRect, TRect Rect, bool Selected, bool Shadowed) +{ + ItemContentsRect(Item, Rect); UnicodeString Text = Pages[Item]->Caption; - if ((Images != NULL) && (Pages[Item]->ImageIndex >= 0)) + if (HasItemImage(Item)) { int Left; if (!Text.IsEmpty()) @@ -231,32 +313,132 @@ void __fastcall TThemePageControl::DrawTabItem(HDC DC, int Item, TRect Rect, std::unique_ptr Canvas(new TCanvas()); Canvas->Handle = DC; Images->Draw(Canvas.get(), Left, Y, Pages[Item]->ImageIndex, !Shadowed); - Rect.Left += Images->Width + 3; - } - else - { - Rect.Left -= 2; } + int TextHeight = 20; int OldMode = SetBkMode(DC, TRANSPARENT); if (!Text.IsEmpty()) { + ItemTextRect(Item, Rect); HFONT OldFont = (HFONT)SelectObject(DC, Font->Handle); - Rect.Right -= 3; wchar_t * Buf = new wchar_t[Text.Length() + 1 + 4]; wcscpy(Buf, Text.c_str()); - TRect TextRect(0, 0, Rect.Right - Rect.Left, 20); + TRect TextRect(0, 0, Rect.Right - Rect.Left, TextHeight); + // Truncates too long texts with ellipsis ::DrawText(DC, Buf, -1, &TextRect, DT_CALCRECT | DT_SINGLELINE | DT_MODIFYSTRING | DT_END_ELLIPSIS); - OffsetRect(&Rect, 0, (Selected ? 0 : -2)); DrawText(DC, Buf, -1, &Rect, DT_NOPREFIX | DT_CENTER); delete[] Buf; + + if (HasTabCloseButton(Item)) + { + Rect = CloseButtonRect(Item); + Rect.Offset(-TabRect.Left, -TabRect.Top); + + if (FHotCloseButton == Item) + { + HBRUSH Brush = CreateSolidBrush(GetSelectedBodyColor()); + FillRect(DC, &Rect, Brush); + DeleteObject(Brush); + + HPEN Pen = CreatePen(PS_SOLID, 1, ColorToRGB(clHighlight)); + HPEN OldPen = static_cast(SelectObject(DC, Pen)); + Rectangle(DC, Rect.Left, Rect.Top, Rect.Right, Rect.Bottom); + SelectObject(DC, OldPen); + 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(FN(BackColor))) + static_cast(FN(CrossColor))) / 5) + COLORREF BlendColor = RGB(BlendValue(GetRValue), BlendValue(GetGValue), BlendValue(GetBValue)); + #undef BlendValue + + TRect CrossRect(Rect); + CrossRect.Inflate(-CrossPadding, -CrossPadding); + + int CrossWidth = ScaleByTextHeight(this, 1); + DrawCross(DC, CrossWidth + 1, BlendColor, CrossRect); + DrawCross(DC, CrossWidth, CrossColor, CrossRect); + } + SelectObject(DC, OldFont); } SetBkMode(DC, OldMode); } //---------------------------------------------------------------------------------------------------------- +int __fastcall TThemePageControl::CloseButtonSize() +{ + return ScaleByTextHeight(this, 16); +} +//---------------------------------------------------------------------------------------------------------- +int __fastcall TThemePageControl::GetCrossPadding() +{ + return ScaleByTextHeight(this, 4); +} +//---------------------------------------------------------------------------------------------------------- +TRect __fastcall TThemePageControl::CloseButtonRect(int Index) +{ + TRect Rect = TabRect(Index); + ItemTabRect(Index, Rect); + ItemContentsRect(Index, Rect); + ItemTextRect(Index, Rect); + + int ACloseButtonSize = CloseButtonSize(); + 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); + if (Index == TabIndex) + { + // To counter Inflate(2, 2) in ItemTabRect + Rect.Left -= 2; + } + Rect.Right = Rect.Left + ACloseButtonSize; + Rect.Bottom = Rect.Top + ACloseButtonSize; + return Rect; +} +//---------------------------------------------------------------------------------------------------------- +void __fastcall TThemePageControl::SetHotCloseButton(int Index) +{ + if (Index != FHotCloseButton) + { + if (FHotCloseButton >= 0) + { + InvalidateTab(FHotCloseButton); + } + FHotCloseButton = Index; + if (FHotCloseButton >= 0) + { + InvalidateTab(FHotCloseButton); + } + } +} +//---------------------------------------------------------------------------------------------------------- +void __fastcall TThemePageControl::MouseMove(TShiftState /*Shift*/, int X, int Y) +{ + SetHotCloseButton(IndexOfCloseButtonAt(X, Y)); +} +//---------------------------------------------------------------------------------------------------------- +int __fastcall TThemePageControl::IndexOfCloseButtonAt(int X, int Y) +{ + int Result = IndexOfTabAt(X, Y); + if ((Result < 0) || + !HasTabCloseButton(Result) || + !CloseButtonRect(Result).Contains(TPoint(X, Y))) + { + Result = -1; + } + return Result; +} +//---------------------------------------------------------------------------------------------------------- void __fastcall TThemePageControl::DrawThemesPart(HDC DC, int PartId, int StateId, LPCWSTR PartNameID, LPRECT Rect) { @@ -267,8 +449,7 @@ void __fastcall TThemePageControl::DrawThemesPart(HDC DC, int PartId, CloseThemeData(Theme); } } -//========================================================================================================== -// these two messages are necessary only to properly redraw deselected tab background, because +//---------------------------------------------------------------------------------------------------------- bool __fastcall TThemePageControl::CanChange() { FOldTabIndex = ActivePageIndex; @@ -303,6 +484,53 @@ void __fastcall TThemePageControl::Change() TPageControl::Change(); } //---------------------------------------------------------------------------------------------------------- +UnicodeString __fastcall TThemePageControl::FormatCaptionWithCloseButton(const UnicodeString & Caption) +{ + int OrigWidth = Canvas->TextWidth(Caption); + UnicodeString Result = Caption; + int CloseButtonWidth = CloseButtonSize(); + while (Canvas->TextWidth(Result) < OrigWidth + CloseButtonWidth) + { + Result += L" "; + } + return Result; +} +//--------------------------------------------------------------------------- +void __fastcall TThemePageControl::WMLButtonDown(TWMLButtonDown & Message) +{ + int Index = IndexOfCloseButtonAt(Message.XPos, Message.YPos); + if (Index >= 0) + { + Message.Result = 1; + if (FOnCloseButtonClick != NULL) + { + FOnCloseButtonClick(this, Index); + } + } + else + { + TPageControl::Dispatch(&Message); + } +} +//--------------------------------------------------------------------------- +void __fastcall TThemePageControl::Dispatch(void * Message) +{ + TMessage * M = reinterpret_cast(Message); + if (M->Msg == CM_MOUSELEAVE) + { + SetHotCloseButton(-1); + TPageControl::Dispatch(Message); + } + else if (M->Msg == WM_LBUTTONDOWN) + { + WMLButtonDown(*reinterpret_cast(M)); + } + else + { + TPageControl::Dispatch(Message); + } +} +//---------------------------------------------------------------------------------------------------------- #ifdef _DEBUG void __fastcall TThemePageControl::RequestAlign() { diff --git a/source/components/ThemePageControl.h b/source/components/ThemePageControl.h index c43abe800..2e343bd63 100644 --- a/source/components/ThemePageControl.h +++ b/source/components/ThemePageControl.h @@ -10,36 +10,64 @@ public: __fastcall TThemeTabSheet(TComponent * Owner); __property bool Shadowed = { read = FShadowed, write = SetShadowed }; + __property bool ShowCloseButton = { read = FShowCloseButton, write = SetShowCloseButton }; private: void __fastcall SetShadowed(bool Value); + void __fastcall SetShowCloseButton(bool Value); + void __fastcall Invalidate(); bool FShadowed; + bool FShowCloseButton; }; //--------------------------------------------------------------------------- +typedef void __fastcall (__closure *TPageControlCloseButtonClick)(TPageControl * Sender, int Index); +//--------------------------------------------------------------------------- class TThemePageControl : public TPageControl { friend class TThemeTabSheet; + +__published: + __property TPageControlCloseButtonClick OnCloseButtonClick = { read = FOnCloseButtonClick, write = FOnCloseButtonClick }; + public: __fastcall TThemePageControl(TComponent * Owner); int __fastcall GetTabsHeight(); + UnicodeString __fastcall FormatCaptionWithCloseButton(const UnicodeString & Caption); protected: virtual void __fastcall PaintWindow(HDC DC); DYNAMIC bool __fastcall CanChange(); DYNAMIC void __fastcall Change(); + DYNAMIC void __fastcall MouseMove(TShiftState Shift, int X, int Y); + virtual void __fastcall Dispatch(void * Message); #ifdef _DEBUG virtual void __fastcall RequestAlign(); #endif private: + void __fastcall DrawThemesXpTab(HDC DC, int Tab); void __fastcall DrawThemesXpTabItem(HDC DC, int Item, const TRect & Rect, bool Body, int State); - void __fastcall DrawTabItem(HDC DC, int Item, TRect Rect, bool Selected, bool Shadowed); + 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 GetCrossPadding(); + TRect __fastcall CloseButtonRect(int Index); + int __fastcall IndexOfCloseButtonAt(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); + void __fastcall WMLButtonDown(TWMLButtonDown & Message); int FOldTabIndex; + int FHotCloseButton; + TPageControlCloseButtonClick FOnCloseButtonClick; }; //--------------------------------------------------------------------------- #endif diff --git a/source/forms/CustomScpExplorer.cpp b/source/forms/CustomScpExplorer.cpp index 0c44d1049..f3d438392 100644 --- a/source/forms/CustomScpExplorer.cpp +++ b/source/forms/CustomScpExplorer.cpp @@ -4672,10 +4672,10 @@ void __fastcall TCustomScpExplorerForm::DuplicateSession() } } //--------------------------------------------------------------------------- -bool __fastcall TCustomScpExplorerForm::CanCloseQueue() +bool __fastcall TCustomScpExplorerForm::CanCloseQueue(TTerminalQueue * Queue) { - DebugAssert(FQueue != NULL); - bool Result = FQueue->IsEmpty; + DebugAssert(Queue != NULL); + bool Result = Queue->IsEmpty; if (!Result) { SetFocus(); @@ -4684,6 +4684,11 @@ bool __fastcall TCustomScpExplorerForm::CanCloseQueue() return Result; } //--------------------------------------------------------------------------- +bool __fastcall TCustomScpExplorerForm::CanCloseQueue() +{ + return CanCloseQueue(FQueue); +} +//--------------------------------------------------------------------------- void __fastcall TCustomScpExplorerForm::CloseSession() { if (CanCloseQueue()) @@ -6256,7 +6261,7 @@ void __fastcall TCustomScpExplorerForm::DoTerminalListChanged() for (int Index = 0; Index <= TerminalList->Count; Index++) { - TTabSheet * TabSheet; + TThemeTabSheet * TabSheet; if (Index >= SessionsPageControl->PageCount) { TabSheet = new TThemeTabSheet(SessionsPageControl); @@ -6264,10 +6269,11 @@ void __fastcall TCustomScpExplorerForm::DoTerminalListChanged() } else { - TabSheet = SessionsPageControl->Pages[Index]; + TabSheet = DebugNotNull(dynamic_cast(SessionsPageControl->Pages[Index])); } - if (Index < TerminalList->Count) + bool IsSessionTab = (Index < TerminalList->Count); + if (IsSessionTab) { TTerminal * Terminal = dynamic_cast(TerminalList->Objects[Index]); TabSheet->Tag = reinterpret_cast(Terminal); @@ -6281,6 +6287,8 @@ void __fastcall TCustomScpExplorerForm::DoTerminalListChanged() // We know that we are at the last page, sotherwise we could not call this (it assumes that new session tab is the last one) UpdateNewSessionTab(); } + + TabSheet->ShowCloseButton = IsSessionTab; } } __finally @@ -6325,7 +6333,7 @@ void __fastcall TCustomScpExplorerForm::UpdateSessionTab(TTabSheet * TabSheet) TabSheet->ImageIndex = AddSessionColor(Color); UnicodeString TabCaption = TTerminalManager::Instance()->GetTerminalTitle(ManagedTerminal, true); - TabSheet->Caption = TabCaption; + TabSheet->Caption = SessionsPageControl->FormatCaptionWithCloseButton(TabCaption); TThemeTabSheet * ThemeTabSheet = dynamic_cast(TabSheet); if (DebugAlwaysTrue(ThemeTabSheet != NULL)) @@ -9843,3 +9851,29 @@ void __fastcall TCustomScpExplorerForm::ReloadDirectory(TOperationSide Side) DirView(Side)->ReloadDirectory(); } //--------------------------------------------------------------------------- +void __fastcall TCustomScpExplorerForm::SessionsPageControlCloseButtonClick(TPageControl * /*Sender*/, int Index) +{ + if (Index == SessionsPageControl->TabIndex) + { + CloseSession(); + } + else + { + TTabSheet * TabSheet = SessionsPageControl->Pages[Index]; + TTerminal * Terminal = GetSessionTabTerminal(TabSheet); + TTerminalManager * Manager = TTerminalManager::Instance(); + if (!Terminal->Active) + { + Manager->FreeTerminal(Terminal); + } + else + { + TTerminalQueue * Queue = Manager->FindQueueForTerminal(Terminal); + if (CanCloseQueue(Queue)) + { + Manager->FreeTerminal(Terminal); + } + } + } +} +//--------------------------------------------------------------------------- diff --git a/source/forms/CustomScpExplorer.dfm b/source/forms/CustomScpExplorer.dfm index ffaaf9cc5..c7eaf2719 100644 --- a/source/forms/CustomScpExplorer.dfm +++ b/source/forms/CustomScpExplorer.dfm @@ -313,6 +313,7 @@ object CustomScpExplorerForm: TCustomScpExplorerForm OnDragDrop = SessionsPageControlDragDrop OnDragOver = SessionsPageControlDragOver OnMouseDown = SessionsPageControlMouseDown + OnCloseButtonClick = SessionsPageControlCloseButtonClick object TabSheet1: TTabSheet Caption = 'TabSheet1' end diff --git a/source/forms/CustomScpExplorer.h b/source/forms/CustomScpExplorer.h index 4618e2b6c..258f936a9 100644 --- a/source/forms/CustomScpExplorer.h +++ b/source/forms/CustomScpExplorer.h @@ -189,6 +189,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); private: TTerminal * FTerminal; @@ -450,6 +451,7 @@ protected: void __fastcall QueueViewDeleteItem(int Index); void __fastcall UserActionTimer(TObject * Sender); void __fastcall UpdateQueueView(); + bool __fastcall CanCloseQueue(TTerminalQueue * Queue); bool __fastcall CanCloseQueue(); virtual bool __fastcall IsFileControl(TObject * Control, TOperationSide Side); virtual void __fastcall ReloadLocalDirectory(const UnicodeString Directory = L""); diff --git a/source/packages/tbx/TBXOfficeXPTheme.pas b/source/packages/tbx/TBXOfficeXPTheme.pas index 8cab200bf..fe63b7873 100644 --- a/source/packages/tbx/TBXOfficeXPTheme.pas +++ b/source/packages/tbx/TBXOfficeXPTheme.pas @@ -86,6 +86,8 @@ type procedure PaintStatusBar(Control: TWinControl; Canvas: TCanvas; R: TRect; Part: Integer); override; end; +function GetSelectedBodyColor: TColor; + implementation uses @@ -1227,7 +1229,7 @@ begin BtnItemColors[bisDisabled, ipText] := DisabledText; SetContrast(BtnItemColors[bisDisabled, ipText], ToolbarColor, 80); BtnItemColors[bisDisabled, ipFrame] := clNone; - BtnItemColors[bisSelected, ipBody] := Blend(clHighlight, Blend(clBtnFace, clWindow, 50), 10); + BtnItemColors[bisSelected, ipBody] := GetSelectedBodyColor; SetContrast(BtnItemColors[bisSelected, ipBody], ToolbarColor, 5); BtnItemColors[bisSelected, ipText] := BtnItemColors[bisNormal, ipText]; BtnItemColors[bisSelected, ipFrame] := clHighlight; @@ -1409,4 +1411,9 @@ begin if Message.WParam = TSC_VIEWCHANGE then SetupColorCache; end; +function GetSelectedBodyColor: TColor; +begin + Result := Blend(clHighlight, Blend(clBtnFace, clWindow, 50), 10); +end; + end.