/** * @file XTPCaption.cpp * * @copyright * (c) 1998-2025 Codejock Software, All Rights Reserved. * * This source file is the property of Codejock Software and must not be * redistributed by any means without the explicit written permission of * Codejock Software. * * The use of this source code is governed by the terms and conditions specified * in the Toolkit Pro license agreement. Codejock Software grants you, as a * single software developer, the limited right to use this software on one * computer only. * * Contact Information: * support@codejock.com * http://www.codejock.com * */ #include "StdAfx.h" #include "Common/XTPTypeId.h" #include "Common/XTPCasting.h" #include "Common/XTPFramework.h" #include "Common/XTPSystemHelpers.h" #include "Common/XTPSynchro.h" #include "Common/XTPApplication.h" #include "Common/XTPSingleton.h" #include "Common/XTPGdiObjects.h" #include "Common/XTPDrawHelpers.h" #include "Common/XTPColorManager.h" #include "Common/XTPResourceManager.h" #include "Common/XTPSystemMetrics.h" #include "Controls/Resource.h" #include "Controls/Defines.h" #include "Controls/Util/XTPControlTheme.h" #include "Controls/Util/XTPUtil.h" #include "Controls/Util/XTPGlobal.h" #include "Controls/Button/XTPButton.h" #include "Controls/Button/XTPButtonTheme.h" #include "Controls/Button/Themes/XTPButtonThemeUltraFlat.h" #include "Controls/Button/Themes/XTPButtonThemeOfficeXP.h" #include "Controls/Button/Themes/XTPButtonThemeOffice2003.h" #include "Controls/Static/XTPCaptionTheme.h" #include "Controls/Static/Themes/XTPCaptionButtonThemeOfficeXP.h" #include "Controls/Static/Themes/XTPCaptionButtonThemeOffice2003.h" #include "Controls/Static/XTPCaption.h" #include "Controls/Static/XTPCaptionPopupWnd.h" #include "Common/Base/Diagnostic/XTPDisableNoisyWarnings.h" #ifdef _DEBUG # define new DEBUG_NEW # undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CXTPCaptionButton CXTPCaptionButton::CXTPCaptionButton() : m_pCaption(NULL) { } ///////////////////////////////////////////////////////////////////////////// // CXTPCaption CXTPCaption::CXTPCaption() : m_pTheme(NULL) { m_bAppCaption = false; m_bUserColors = false; m_strCaption = _T(""); m_hIcon = NULL; m_pChildWnd = NULL; m_pParentView = NULL; m_pSplitterWnd = NULL; m_pPopupWnd = NULL; m_nOffset = 0; m_nBorder = 0; m_sizeIcon = XTPSystemMetrics()->GetSmallIconSize(); m_dwExStyle = 0L; m_clrText = ::GetSysColor(COLOR_BTNTEXT); m_clrBorder = ::GetSysColor(COLOR_3DFACE); m_clrFace = ::GetSysColor(COLOR_3DFACE); VERIFY(SetTheme(xtpControlThemeDefault)); } CXTPCaption::~CXTPCaption() { CMDTARGET_RELEASE(m_pTheme); } IMPLEMENT_DYNAMIC(CXTPCaption, CStatic) #include "Common/Base/Diagnostic/XTPBeginAfxMap.h" BEGIN_MESSAGE_MAP(CXTPCaption, CStatic) //{{AFX_MSG_MAP(CXTPCaption) ON_WM_PAINT() ON_MESSAGE(WM_PRINTCLIENT, OnPrintClient) ON_WM_ERASEBKGND() ON_WM_WINDOWPOSCHANGED() ON_WM_SYSCOLORCHANGE() ON_WM_SIZE() //}}AFX_MSG_MAP ON_BN_CLICKED(XTP_IDC_BTN_CLOSE, OnCaptButton) ON_MESSAGE_VOID(CPN_XTP_PUSHPINBUTTON, OnPushPinButton) ON_MESSAGE_VOID(CPN_XTP_PUSHPINCANCEL, OnPushPinCancel) ON_MESSAGE(WM_XTP_SETCONTROLTHEME, OnSetTheme) END_MESSAGE_MAP() #include "Common/Base/Diagnostic/XTPEndAfxMap.h" void CXTPCaption::RefreshMetrics() { if (m_pTheme) m_pTheme->RefreshMetrics(this); if (::IsWindow(m_hWnd)) RedrawWindow(); } BOOL CXTPCaptionButton::SetTheme(XTPControlTheme eTheme) { CMDTARGET_RELEASE(m_pTheme); switch (eTheme) { case xtpControlThemeOfficeXP: m_pTheme = new CXTPCaptionButtonThemeOfficeXP(); break; case xtpControlThemeOffice2003: m_pTheme = new CXTPCaptionButtonThemeOffice2003(); break; default: m_pTheme = new CXTPCaptionButtonTheme(); } RefreshMetrics(); return (m_pTheme != NULL); } BOOL CXTPCaption::SetTheme(CXTPCaptionTheme* pTheme) { CMDTARGET_RELEASE(m_pTheme); m_pTheme = pTheme; m_btnCaption.SetTheme(pTheme->GetThemeStyle()); RefreshMetrics(); return (m_pTheme != NULL); } BOOL CXTPCaption::SetTheme(XTPControlTheme eTheme) { switch (eTheme) { case xtpControlThemeOfficeXP: return SetTheme(new CXTPCaptionThemeOfficeXP()); case xtpControlThemeOffice2003: return SetTheme(new CXTPCaptionThemeOffice2003()); } return SetTheme(new CXTPCaptionTheme()); } LRESULT CXTPCaption::OnSetTheme(WPARAM wParam, LPARAM /*lParam*/) { XTPControlTheme eTheme = (XTPControlTheme)wParam; SetTheme(eTheme); return 0; } BOOL CXTPCaption::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) { if (message == WM_SETTINGCHANGE || message == WM_SYSCOLORCHANGE) { RefreshMetrics(); } return CStatic::OnWndMsg(message, wParam, lParam, pResult); } void CXTPCaption::OnSize(UINT nType, int cx, int cy) { CStatic::OnSize(nType, cx, cy); if (m_hIcon != NULL) RedrawWindow(); } void CXTPCaption::OnPaint() { // background is already filled in gray CPaintDC dc(this); // Get the client rect. CXTPClientRect rectClient(this); // Paint to a memory device context to help // eliminate screen flicker. CXTPBufferDC memDC(dc); // If the caption button is a valid window and visible exclude from painting... if (::IsWindow(m_btnCaption.m_hWnd) && m_btnCaption.IsWindowVisible()) { CXTPWindowRect rcButton(&m_btnCaption); ScreenToClient(&rcButton); memDC.ExcludeClipRect(&rcButton); } // draw the background, text and icon. DrawCaptionBack(&memDC, rectClient); DrawCaptionText(&memDC); DrawCaptionIcon(&memDC, rectClient); } LRESULT CXTPCaption::OnPrintClient(WPARAM wParam, LPARAM /*lParam*/) { CDC* pDC = CDC::FromHandle((HDC)wParam); if (pDC) { CXTPClientRect rectClient(this); DrawCaptionBack(pDC, rectClient); DrawCaptionText(pDC); DrawCaptionIcon(pDC, rectClient); } return 1; } BOOL CXTPCaption::OnEraseBkgnd(CDC* /*pDC*/) { return TRUE; } void CXTPCaption::DrawCaptionBack(CDC* pDC, CRect& rcItem) { GetTheme()->DrawCaptionBack(pDC, this, rcItem); } void CXTPCaption::DrawCaptionText(CDC* pDC) { GetTheme()->DrawCaptionText(pDC, this); } void CXTPCaption::DrawCaptionIcon(CDC* pDC, CRect& rcItem) { GetTheme()->DrawCaptionIcon(pDC, this, rcItem); } BOOL CXTPCaption::Create(CWnd* pParentWnd, LPCTSTR lpszWindowName, DWORD dwExStyle, DWORD dwStyle, const CRect& rect, UINT nID) { // Let the base class create the control. if (!CStatic::Create(NULL, dwStyle | WS_CLIPCHILDREN, rect, pParentWnd, nID)) { TRACE(_T("Unable to create caption.\n")); return FALSE; } SetFont(&XTPAuxData().xtpFont); // save the style. m_dwExStyle = (dwExStyle & (CPWS_EX_GROOVE_EDGE | CPWS_EX_RAISED_EDGE | CPWS_EX_CLOSEBUTTON)); ModifyStyleEx(0, (dwExStyle & ~m_dwExStyle)); // Save the window text. m_strCaption = lpszWindowName; if (HasCloseButton()) { // Create the caption's close button. if (!m_btnCaption.Create(NULL, WS_VISIBLE | WS_CHILD | BS_ICON | BS_CENTER | BS_VCENTER, CRect(0, 0, 0, 0), this, XTP_IDC_BTN_CLOSE)) { TRACE0("Unable to create caption button.\n"); return -1; } CString xaml; int id = XTPColorManager()->GetCurrentSystemTheme() == xtpSystemThemeAero ? IDR_XAML_ICON_CAPTION_CLOSE_2 : IDR_XAML_ICON_CAPTION_CLOSE_1; VERIFY(XTPResourceManager()->LoadXAML(xaml, XTPToUInt(id))); m_btnCaption.EnableMarkup(); m_btnCaption.SetWindowText(xaml); } else { // Create the button to be used with child window. if (!m_btnCaption.Create(m_strCaption, BS_ICON | BS_VCENTER, CRect(0, 0, 0, 0), this, XTP_IDC_BTN_CLOSE)) { TRACE0("Unable to create caption button.\n"); return FALSE; } m_bAppCaption = true; } m_btnCaption.SetCaption(this); SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED); return TRUE; } void CXTPCaption::ModifyCaptionStyle(int nBorderSize, CFont* pFont, LPCTSTR lpszWindText, HICON hIcon) { _ASSERTE(::IsWindow(m_hWnd)); if (nBorderSize != 0) { m_nBorder = nBorderSize; } if (pFont != NULL) { SetFont(pFont); // button font should match caption if (::IsWindow(m_btnCaption.m_hWnd)) m_btnCaption.SetFont(GetFont()); } if (lpszWindText != NULL) { m_strCaption = lpszWindText; } if (hIcon != NULL) { m_hIcon = hIcon; } Invalidate(); } void CXTPCaption::SetChildWindow(CWnd* pChild, CWnd* pNotifyWnd) { _ASSERTE(pChild); // must be valid. _ASSERTE(pNotifyWnd); // must be valid. // save window pointers, and show caption button. m_pChildWnd = pChild; SetOwner(pNotifyWnd); m_pParentView = pChild->GetParent(); ASSERT_KINDOF(CView, m_pParentView); m_pSplitterWnd = m_pParentView->GetParent(); ASSERT_KINDOF(CSplitterWnd, m_pSplitterWnd); // get the size of the child and parent windows. m_pChildWnd->GetClientRect(&m_rcChild); m_pParentView->GetClientRect(&m_rcParent); m_pSplitterWnd->GetClientRect(&m_rcSplitter); // save the size of the childs caption area. m_nOffset = (m_rcParent.Height() - m_rcChild.Height()); // make the caption button visible. m_btnCaption.ShowWindow(SW_SHOW); m_btnCaption.UpdateWindow(); // inflate the size of the parent to add a border, this will // also be the size of the popup window. int cx = ::GetSystemMetrics(SM_CXEDGE) * 2; int cy = ::GetSystemMetrics(SM_CYEDGE) * 2; m_rcParent.InflateRect(cx, cy); } void CXTPCaption::KillChildWindow() { // Hide the caption button. if (::IsWindow(m_btnCaption.m_hWnd)) { m_btnCaption.ShowWindow(SW_HIDE); m_btnCaption.SetState(FALSE); } // Destroy the pop-up window. if (::IsWindow(m_pPopupWnd->GetSafeHwnd())) { m_pPopupWnd->DestroyWindow(); } SAFE_DELETE(m_pPopupWnd); } BOOL CXTPCaption::ShowPopupWindow() { // already visible return TRUE. if (::IsWindowVisible(m_pPopupWnd->GetSafeHwnd())) return TRUE; // if not a window yet create it. if (!::IsWindow(m_pPopupWnd->GetSafeHwnd())) { m_pPopupWnd = new CXTPCaptionPopupWnd; if (!m_pPopupWnd->Create(CRect(0, 0, 0, 0), this, m_pChildWnd)) { SAFE_DELETE(m_pPopupWnd); return FALSE; } } // view pointer is not valid. if (!::IsWindow(m_pParentView->GetSafeHwnd())) return FALSE; // splitter pointer is not valid. if (!::IsWindow(m_pSplitterWnd->GetSafeHwnd())) return FALSE; CRect rcPopup(m_rcParent); m_pParentView->ClientToScreen(&rcPopup); // get the current size of the splitter window. CRect rcSplitter; m_pSplitterWnd->GetClientRect(&rcSplitter); // check to see if the size has changed. if (rcSplitter.Height() > m_rcSplitter.Height()) { rcPopup.bottom += (rcSplitter.Height() - m_rcSplitter.Height()); } else if (m_rcSplitter.Height() > rcSplitter.Height()) { rcPopup.bottom -= (m_rcSplitter.Height() - rcSplitter.Height()); } // display the popup window. m_pPopupWnd->MoveWindow(&rcPopup); m_pPopupWnd->ShowWindow(SW_SHOW); m_pPopupWnd->RecalcLayout(); m_pPopupWnd->SetTheme(m_pTheme->GetThemeStyle()); // press the caption button. m_btnCaption.SetState(TRUE); return TRUE; } void CXTPCaption::OnCaptButton() { if (HasCloseButton()) { CWnd* pOwner = GetOwner(); _ASSERTE(pOwner); if (pOwner) { // Notify of selection change. pOwner->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd); } } else { ShowPopupWindow(); } } void CXTPCaption::OnPushPinButton() { KillChildWindow(); // get the current size of the child's parent window and // adjust the size of the child accordingly. m_pParentView->GetClientRect(&m_rcParent); m_rcChild = m_rcParent; m_rcChild.top += m_nOffset; // move the child window to its correct location. m_pChildWnd->MoveWindow(m_rcChild); CWnd* pNotifyWnd = GetOwner(); _ASSERTE(pNotifyWnd); if (::IsWindow(pNotifyWnd->GetSafeHwnd())) { // Notify that push pin button was pressed. pNotifyWnd->SendMessage(CPN_XTP_PUSHPINBUTTON); Invalidate(); } } void CXTPCaption::OnPushPinCancel() { m_btnCaption.SetState(FALSE); m_pPopupWnd->DestroyWindow(); SAFE_DELETE(m_pPopupWnd); CWnd* pNotifyWnd = GetOwner(); _ASSERTE(pNotifyWnd); if (::IsWindow(pNotifyWnd->GetSafeHwnd())) { // Notify that popup window has lost input focus. pNotifyWnd->SendMessage(CPN_XTP_PUSHPINCANCEL); Invalidate(); } } void CXTPCaption::UpdateCaption(LPCTSTR lpszWindowText, HICON hIcon) { _ASSERTE(::IsWindow(m_hWnd)); CRect rcClient; GetClientRect(&rcClient); CDC* pDC = GetDC(); DrawCaptionBack(pDC, rcClient); if (lpszWindowText != NULL) { m_strCaption = lpszWindowText; UpdateCaptionText(pDC); } if (hIcon != NULL) { m_hIcon = hIcon; DrawCaptionIcon(pDC, rcClient); } ReleaseDC(pDC); } void CXTPCaption::UpdateCaptionText(CDC* pDC) { if (::IsWindow(m_btnCaption.m_hWnd)) { m_btnCaption.SetWindowText(m_strCaption); m_btnCaption.MoveWindow(GetButtonRect()); } DrawCaptionText(pDC); } CRect CXTPCaption::GetTextRect() const { if (m_strCaption.IsEmpty()) return CXTPEmptyRect(); CXTPClientRect rcClient(this); // Use a NULL window dc to get the size the caption button // should be using CDC::GetTextExtent(...). CWindowDC dc(NULL); CXTPFontDC fontDC(&dc, GetFont()); TEXTMETRIC tm; dc.GetTextMetrics(&tm); // Calculate the button area size. CSize size = dc.GetTextExtent(m_strCaption, m_strCaption.GetLength()); size.cx += tm.tmAveCharWidth + XTP_DPI_X(12); size.cy = rcClient.Height(); int iWidth = rcClient.Width() - (m_hIcon ? XTP_DPI_X(18) : 0); if (size.cx > iWidth) size.cx = iWidth; return CRect(m_nBorder, m_nBorder, m_nBorder + size.cx, size.cy - m_nBorder); } CRect CXTPCaption::GetButtonRect() const { if (!::IsWindow(m_btnCaption.m_hWnd)) return CXTPEmptyRect(); if (!HasCloseButton()) return GetTextRect(); CXTPClientRect rcClient(this); CRect rcButton; rcButton.top = (rcClient.Height() - m_sizeIcon.cy) / 2; rcButton.bottom = rcButton.top + m_sizeIcon.cy; // ensure offset is the same for top and right edge. int x = rcButton.top - rcClient.top; rcButton.left = (rcClient.Width() - m_sizeIcon.cx) - x; rcButton.right = rcButton.left + m_sizeIcon.cx; return rcButton; } void CXTPCaption::OnWindowPosChanged(WINDOWPOS FAR* lpwndpos) { CStatic::OnWindowPosChanged(lpwndpos); if (::IsWindow(m_btnCaption.m_hWnd)) m_btnCaption.MoveWindow(GetButtonRect()); } void CXTPCaption::SetCaptionColors(COLORREF clrBorder, COLORREF clrFace, COLORREF clrText) { _ASSERTE(::IsWindow(m_hWnd)); m_bUserColors = true; m_clrBorder = clrBorder; m_clrFace = clrFace; m_clrText = clrText; } void CXTPCaption::SetOffice2003Colors(bool b2003Colors /*= true*/) { m_bUserColors = b2003Colors; SetTheme(b2003Colors ? xtpControlThemeOffice2003 : xtpControlThemeDefault); } void CXTPCaption::OnSysColorChange() { CStatic::OnSysColorChange(); // update caption colors if not user defined. if (m_bUserColors == false) { m_clrText = ::GetSysColor(COLOR_BTNTEXT); m_clrBorder = ::GetSysColor(COLOR_3DFACE); m_clrFace = ::GetSysColor(COLOR_3DFACE); } }