1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00
Files
winscp/source/components/ThemePageControl.cpp
Martin Prikryl 0b4c19ead3 5.2.4
2018-01-11 15:31:27 +01:00

299 lines
9.6 KiB
C++

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include <Common.h>
#include <vsstyle.h>
#include "ThemePageControl.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
// Based on
// http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=6355
//---------------------------------------------------------------------------
//#define USE_DEFAULT_XP_TOPTAB // XP top tab is drawn only for test purpose. To use default, uncoment this line
//---------------------------------------------------------------------------
// constant string definitions here (or you can put it into resource string table)
#define IDS_UTIL_TAB L"TAB"
#define IDS_UTIL_UXTHEME L"UxTheme.dll"
#define IDS_UTIL_THEMEACT "IsThemeActive"
#define IDS_UTIL_THEMEOPN "OpenThemeData"
#define IDS_UTIL_THEMEBCKG "DrawThemeBackground"
//---------------------------------------------------------------------------
static inline void ValidCtrCheck(TThemePageControl *)
{
new TThemePageControl(NULL);
}
//---------------------------------------------------------------------------
namespace Themepagecontrol
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TThemePageControl)};
RegisterComponents(L"Scp", classes, 0);
}
}
//----------------------------------------------------------------------------------------------------------
__fastcall TThemeTabSheet::TThemeTabSheet(TComponent * Owner) :
TTabSheet(Owner)
{
FShadowed = false;
}
//----------------------------------------------------------------------------------------------------------
void __fastcall TThemeTabSheet::SetShadowed(bool Value)
{
if (Shadowed != Value)
{
FShadowed = Value;
TThemePageControl * ThemePageControl = dynamic_cast<TThemePageControl *>(Parent);
if (ALWAYS_TRUE(ThemePageControl != NULL))
{
ThemePageControl->InvalidateTab(TabIndex);
}
else
{
Parent->Invalidate();
}
}
}
//----------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------
__fastcall TThemePageControl::TThemePageControl(TComponent * Owner) :
TPageControl(Owner)
{
FOldTabIndex = -1;
}
//----------------------------------------------------------------------------------------------------------
int __fastcall TThemePageControl::GetTabsHeight()
{
// Calculated height includes tab/contents separator line on Windows 7/8,
// but not on Windows XP
TRect Rect = GetClientRect();
::SendMessage(Handle, TCM_ADJUSTRECT, FALSE, (LPARAM)&Rect);
int Result = Rect.Top - 1;
// two different ways to calculate the same, not sure which one is more reliable,
// so we want to know in case they differ
if (ALWAYS_TRUE(PageCount >= 0))
{
TRect Rect = TabRect(0);
int Result2 = Rect.Bottom + 1;
if (ALWAYS_FALSE(Result != Result2))
{
Result = Result2;
}
}
return Result;
}
//----------------------------------------------------------------------------------------------------------
void __fastcall TThemePageControl::PaintWindow(HDC DC)
{
// Themes not enabled, give up
if (!UseThemes())
{
TPageControl::PaintWindow(DC);
return;
}
// TODO use GetClipBox
TRect PageRect = GetClientRect();
// 1st paint the tab body
TRect ClientRect = PageRect;
::SendMessage(Handle, TCM_ADJUSTRECT, FALSE, (LPARAM)&PageRect);
ClientRect.Top = PageRect.Top - 2;
DrawThemesXpTabItem(DC, -1, ClientRect, true, 0);
// 2nd paint the inactive tabs
TPoint Point = ScreenToClient(Mouse->CursorPos);
int HotIndex = IndexOfTabAt(Point.X, Point.Y);
int SelectedIndex = TabIndex;
for (int Tab = 0; Tab < PageCount; Tab++)
{
if (Tab != SelectedIndex)
{
TThemeTabSheet * ThemeTabSheet = dynamic_cast<TThemeTabSheet *>(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);
}
}
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);
}
}
//----------------------------------------------------------------------------------------------------------
// 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)
{
TSize Size = Rect.Size;
// Draw background
HDC DCMem = CreateCompatibleDC(DC);
HBITMAP BitmapMem = CreateCompatibleBitmap(DC, Size.Width, Size.Height);
HBITMAP BitmapOld = (HBITMAP)SelectObject(DCMem, BitmapMem);
TRect RectMem(0, 0, Size.Width, Size.Height);
if (!Body && (State == TIS_SELECTED))
{
RectMem.Bottom++;
}
if (Body)
{
DrawThemesPart(DCMem, TABP_PANE, State, IDS_UTIL_TAB, &RectMem);
}
else
{
DrawThemesPart(DCMem, TABP_TABITEM, State, IDS_UTIL_TAB, &RectMem);
}
// Init some extra parameters
BITMAPINFO BitmapInfo;
// Fill local pixel arrays
ZeroMemory(&BitmapInfo, sizeof(BITMAPINFO));
BITMAPINFOHEADER & BitmapInfoHeader = BitmapInfo.bmiHeader;
BitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfoHeader.biCompression = BI_RGB;
BitmapInfoHeader.biPlanes = 1;
// force as RGB: 3 bytes,24 bits -> good for rotating bitmap in any resolution
BitmapInfoHeader.biBitCount = 24;
BitmapInfoHeader.biWidth = Size.Width;
BitmapInfoHeader.biHeight = Size.Height;
if (!Body && (Item >= 0))
{
if ((State == TIS_SELECTED))
{
RectMem.Bottom--;
}
DrawTabItem(DCMem, Item, RectMem, (State == TIS_SELECTED));
}
// Blit image to the screen
BitBlt(DC, Rect.Left, Rect.Top, Size.Width, Size.Height, DCMem, 0, 0, SRCCOPY);
SelectObject(DCMem, BitmapOld);
DeleteObject(BitmapMem);
DeleteDC(DCMem);
}
//----------------------------------------------------------------------------------------------------------
// draw tab item context: possible icon and text
void __fastcall TThemePageControl::DrawTabItem(HDC DC, int Item, TRect Rect, bool Selected)
{
if (Selected)
{
Rect.Bottom -= 1;
}
else
{
Rect.Bottom += 2;
}
Rect.Left += 6;
Rect.Top += 2 + (Selected ? 1 : 3);
UnicodeString Text = Pages[Item]->Caption;
int OldMode = SetBkMode(DC, TRANSPARENT);
if ((Images != NULL) && (Pages[Item]->ImageIndex >= 0))
{
int Left;
if (!Text.IsEmpty())
{
Left = Rect.Left + (Selected ? 2 : 0);
}
else
{
Left = (Rect.Right - Images->Width - Rect.Left) / 2;
}
int Y = ((Rect.Top + Rect.Bottom - Images->Height) / 2) - 1 + (Selected ? 0 : -2);
ImageList_Draw((HIMAGELIST)Images->Handle, Pages[Item]->ImageIndex, DC,
Left, Y, ILD_TRANSPARENT);
Rect.Left += Images->Width + 3;
}
else
{
Rect.Left -= 2;
}
if (!Text.IsEmpty())
{
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);
::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;
SelectObject(DC, OldFont);
}
SetBkMode(DC, OldMode);
}
//----------------------------------------------------------------------------------------------------------
void __fastcall TThemePageControl::DrawThemesPart(HDC DC, int PartId,
int StateId, LPCWSTR PartNameID, LPRECT Rect)
{
HTHEME Theme = OpenThemeData(NULL, PartNameID);
if (Theme != 0)
{
DrawThemeBackground(Theme, DC, PartId, StateId, Rect, NULL);
CloseThemeData(Theme);
}
}
//==========================================================================================================
// these two messages are necessary only to properly redraw deselected tab background, because
bool __fastcall TThemePageControl::CanChange()
{
FOldTabIndex = ActivePageIndex;
return TPageControl::CanChange();
}
//----------------------------------------------------------------------------------------------------------
void __fastcall TThemePageControl::InvalidateTab(int Index)
{
if (HandleAllocated())
{
TRect Rect = TabRect(Index);
if (Index == TabIndex)
{
Rect.Inflate(2, 2);
}
// Original code was invalidating range against parent window
// (recalculating coordinates first)
InvalidateRect(Handle, &Rect, true);
}
}
//----------------------------------------------------------------------------------------------------------
void __fastcall TThemePageControl::Change()
{
// note that TabIndex yields correct value already here,
// while ActivePageIndex is not updated yet
if ((FOldTabIndex >= 0) && (FOldTabIndex != TabIndex) && UseThemes())
{
InvalidateTab(FOldTabIndex);
}
TPageControl::Change();
}
//----------------------------------------------------------------------------------------------------------