| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 | 
							- //---------------------------------------------------------------------------
 
- #include <vcl.h>
 
- #pragma hdrstop
 
- #include <Common.h>
 
- #include <vsstyle.h>
 
- #include <memory>
 
- #include <PasTools.hpp>
 
- #include <TBXOfficeXPTheme.hpp>
 
- #include "ThemePageControl.h"
 
- //---------------------------------------------------------------------------
 
- #pragma package(smart_init)
 
- //---------------------------------------------------------------------------
 
- // Based on
 
- // https://www.codeproject.com/Articles/6355/XP-Themes-Tab-Control-in-any-orientation
 
- //---------------------------------------------------------------------------
 
- //#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;
 
-   FShowCloseButton = false;
 
- }
 
- //----------------------------------------------------------------------------------------------------------
 
- void __fastcall TThemeTabSheet::Invalidate()
 
- {
 
-   TThemePageControl * ThemePageControl = dynamic_cast<TThemePageControl *>(Parent);
 
-   if (DebugAlwaysTrue(ThemePageControl != NULL))
 
-   {
 
-     ThemePageControl->InvalidateTab(TabIndex);
 
-   }
 
-   else
 
-   {
 
-     Parent->Invalidate();
 
-   }
 
- }
 
- //----------------------------------------------------------------------------------------------------------
 
- void __fastcall TThemeTabSheet::SetShadowed(bool Value)
 
- {
 
-   if (Shadowed != Value)
 
-   {
 
-     FShadowed = Value;
 
-     Invalidate();
 
-   }
 
- }
 
- //----------------------------------------------------------------------------------------------------------
 
- void __fastcall TThemeTabSheet::SetShowCloseButton(bool Value)
 
- {
 
-   if (ShowCloseButton != Value)
 
-   {
 
-     FShowCloseButton = Value;
 
-     Invalidate();
 
-   }
 
- }
 
- //----------------------------------------------------------------------------------------------------------
 
- //----------------------------------------------------------------------------------------------------------
 
- __fastcall TThemePageControl::TThemePageControl(TComponent * Owner) :
 
-   TPageControl(Owner)
 
- {
 
-   FOldTabIndex = -1;
 
-   FHotCloseButton = -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 (DebugAlwaysTrue(PageCount >= 0))
 
-   {
 
-     TRect Rect = TabRect(0);
 
-     int Result2 = Rect.Bottom + 1;
 
-     // On Windows 10 with 200% scaling, the first is 40, the second is 42.
 
-     // With 250% scaling its 50 vs 53.
 
-     // Using the larger.
 
-     if (Result2 > Result)
 
-     {
 
-       DebugAssert(IsWin10());
 
-       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
 
-   int SelectedIndex = TabIndex; // optimization
 
-   for (int Tab = 0; Tab < PageCount; Tab++)
 
-   {
 
-     if (Tab != SelectedIndex)
 
-     {
 
-       DrawThemesXpTab(DC, Tab);
 
-     }
 
-   }
 
-   if (SelectedIndex >= 0)
 
-   {
 
-     DrawThemesXpTab(DC, TabIndex);
 
-   }
 
- }
 
- //----------------------------------------------------------------------------------------------------------
 
- bool __fastcall TThemePageControl::HasTabCloseButton(int Index)
 
- {
 
-   TThemeTabSheet * ThemeTabSheet = dynamic_cast<TThemeTabSheet *>(Pages[Index]);
 
-   return (ThemeTabSheet != NULL) ? ThemeTabSheet->ShowCloseButton : false;
 
- }
 
- //----------------------------------------------------------------------------------------------------------
 
- void __fastcall TThemePageControl::DrawThemesXpTab(HDC DC, int Tab)
 
- {
 
-   TThemeTabSheet * ThemeTabSheet = dynamic_cast<TThemeTabSheet *>(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)
 
- {
 
-   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);
 
-   TRect RectItemMem(RectMem);
 
-   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))
 
-   {
 
-     DrawTabItem(DCMem, Item, Rect, RectItemMem, (State == TIS_SELECTED), (State == TIS_DISABLED));
 
-   }
 
-   // 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);
 
- }
 
- //----------------------------------------------------------------------------------------------------------
 
- 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 -= 2;
 
-     Rect.Top += 1;
 
-   }
 
-   else
 
-   {
 
-     Rect.Bottom += 2;
 
-     Rect.Top += 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<HPEN>(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 (HasItemImage(Item))
 
-   {
 
-     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);
 
-     std::unique_ptr<TCanvas> Canvas(new TCanvas());
 
-     Canvas->Handle = DC;
 
-     Images->Draw(Canvas.get(), Left, Y, Pages[Item]->ImageIndex, !Shadowed);
 
-   }
 
-   int TextHeight = 20;
 
-   int OldMode = SetBkMode(DC, TRANSPARENT);
 
-   if (!Text.IsEmpty())
 
-   {
 
-     ItemTextRect(Item, Rect);
 
-     HFONT OldFont = (HFONT)SelectObject(DC, Font->Handle);
 
-     wchar_t * Buf = new wchar_t[Text.Length() + 1 + 4];
 
-     wcscpy(Buf, Text.c_str());
 
-     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);
 
-     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<HPEN>(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<int>(FN(BackColor))) + static_cast<int>(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)
 
- {
 
-   HTHEME Theme = OpenThemeData(NULL, PartNameID);
 
-   if (Theme != 0)
 
-   {
 
-     DrawThemeBackground(Theme, DC, PartId, StateId, Rect, NULL);
 
-     CloseThemeData(Theme);
 
-   }
 
- }
 
- //----------------------------------------------------------------------------------------------------------
 
- 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();
 
- }
 
- //----------------------------------------------------------------------------------------------------------
 
- 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<TMessage*>(Message);
 
-   if (M->Msg == CM_MOUSELEAVE)
 
-   {
 
-     SetHotCloseButton(-1);
 
-     TPageControl::Dispatch(Message);
 
-   }
 
-   else if (M->Msg == WM_LBUTTONDOWN)
 
-   {
 
-     WMLButtonDown(*reinterpret_cast<TWMLButtonDown *>(M));
 
-   }
 
-   else
 
-   {
 
-     TPageControl::Dispatch(Message);
 
-   }
 
- }
 
- //----------------------------------------------------------------------------------------------------------
 
- #ifdef _DEBUG
 
- void __fastcall TThemePageControl::RequestAlign()
 
- {
 
-   TPageControl::RequestAlign();
 
- }
 
- #endif
 
- //----------------------------------------------------------------------------------------------------------
 
 
  |