/** * @file XTPPopupControl.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/Resource.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/XTPImageManager.h" #include "Common/XTPVC80Helpers.h" #include "Common/XTPMarkupRender.h" #include "Common/XTPResourceManager.h" #include "Common/XTPColorManager.h" #include "Controls/Defines.h" #include "Controls/Popup/XTPPopupControl.h" #include "Controls/Popup/XTPPopupItem.h" #include "Controls/Popup/XTPPopupPaintManager.h" #include "Themes/XTPPopupThemeOffice2000.h" #include "Themes/XTPPopupThemeOfficeXP.h" #include "Themes/XTPPopupThemeOffice2003.h" #include "Themes/XTPPopupThemeOffice2013.h" #include "Themes/XTPPopupThemeMSN.h" #include "Themes/XTPPopupThemeResource.h" #include "Common/Base/Diagnostic/XTPDisableNoisyWarnings.h" #ifdef _DEBUG # define new DEBUG_NEW # undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define TID_EXPANDING 0x128L #define TID_COLLAPSING 0x129L #define TID_SHOWDELAY 0x130L // this define from new SDK implementation // not supported by Visual C++ 6.0 #ifndef LWA_ALPHA # define LWA_ALPHA 0x00000002 #endif #ifndef WS_EX_LAYERED # define WS_EX_LAYERED 0x00080000 #endif // end #ifndef IDC_HAND # define IDC_HAND MAKEINTRESOURCE(32649) #endif ///////////////////////////////////////////////////////////////////////////// // CXTPPopupControl CXTPPopupControl::CXTPPopupControl(BOOL bAutoDelete /*=FALSE*/) : m_nID(0) , m_bAutoDelete(bAutoDelete) { // set default paint manager m_pPaintManager = new CXTPPopupThemeOffice2000(); m_paintTheme = xtpPopupThemeOffice2000; m_pPaintManager->RefreshMetrics(); // init handels and flags for mouse operation m_pSelected = NULL; m_pPressed = NULL; m_bCapture = FALSE; // default state popup window m_popupAnimation = xtpPopupAnimationNone; m_popupState = xtpPopupStateClosed; // select def transparency value m_nCurrentTransparency = m_nTransparency = 255; m_pfnSetLayeredWindowAttributes = NULL; m_pfnUpdateLayeredWindow = NULL; m_bLayered = FALSE; // init animation vars m_nAnimationInterval = 16; m_uShowDelay = 5000L; m_uAnimationDelay = 256L; m_nStep = 0; m_bRightToLeft = FALSE; m_nBackgroundBitmap = 0; // popup pos&size init m_szPopup = XTP_DPI(CSize(170, 130)); m_ptPopup = CPoint(-1, -1); m_bSplashScreenMode = FALSE; // init layered function (for Win98 compatible) HMODULE hLib = GetModuleHandle(_T("USER32")); if (hLib) { m_pfnSetLayeredWindowAttributes = XTPToFunctionPtr( ::GetProcAddress(hLib, "SetLayeredWindowAttributes")); m_pfnUpdateLayeredWindow = XTPToFunctionPtr( ::GetProcAddress(hLib, "UpdateLayeredWindow")); } m_bAllowMove = FALSE; m_pImageManager = new CXTPImageManager; m_pMarkupContext = NULL; m_nPopupLocation = xtpPopupLocationNearTaskBar; // load the system hand cursor for hyperlink items. m_hHandCursor = AfxGetApp()->LoadStandardCursor(IDC_HAND); // if not found, use the toolkit version if (m_hHandCursor == NULL) m_hHandCursor = XTPResourceManager()->LoadCursor(XTP_IDC_HAND); m_bDpiBitmapScaling = FALSE; m_bDpiBitmapScalingModified = FALSE; } CXTPPopupControl::~CXTPPopupControl() { // Destroy CWnd object Close(); // clear all items RemoveAllItems(); // delete paint manager if (m_pPaintManager) delete m_pPaintManager; if (m_pImageManager) m_pImageManager->InternalRelease(); XTPMarkupReleaseContext(m_pMarkupContext, TRUE); } void CXTPPopupControl::PostNcDestroy() { if (m_bAutoDelete) delete this; } CPoint CXTPPopupControl::GetPopupPos() const { if (m_ptPopup != CPoint(-1, -1)) return m_ptPopup; if (m_nPopupLocation == xtpPopupLocationNearTaskBar) { APPBARDATA abd; ZeroMemory(&abd, sizeof(APPBARDATA)); abd.cbSize = sizeof(APPBARDATA); if (SHAppBarMessage(ABM_GETTASKBARPOS, &abd)) { CRect rc = XTPMultiMonitor()->GetWorkArea(&abd.rc); if (rc.CenterPoint().y < abd.rc.top) return CPoint(rc.right, rc.bottom); if (rc.CenterPoint().x > abd.rc.right) return CPoint(rc.left + m_szPopup.cx, rc.bottom); if (rc.CenterPoint().y > abd.rc.bottom) return CPoint(rc.right, rc.top + m_szPopup.cy); if (rc.CenterPoint().x < abd.rc.left) return CPoint(rc.right, rc.bottom); } } // get desktop parameters CRect rcDeskWnd; ::SystemParametersInfo(SPI_GETWORKAREA, 0, &rcDeskWnd, 0); if (m_nPopupLocation == xtpPopupLocationCenter) { return CPoint(rcDeskWnd.CenterPoint().x + m_szPopup.cx / 2, rcDeskWnd.CenterPoint().y + m_szPopup.cy / 2); } // set position return rcDeskWnd.BottomRight(); } void CXTPPopupControl::SetRegionAlphaLayer(CXTPImageManagerIcon* pIcon) { if (!pIcon) return; if (!pIcon->IsRasterIcon()) return; if (!m_pfnUpdateLayeredWindow) return; CXTPImageManagerIconHandle hShadow; hShadow.CopyHandle(pIcon->GetIcon()); if (!hShadow.PreMultiply()) return; PBYTE pBits = hShadow.PreMultiply(); CSize szIcon = hShadow.GetExtent(); int cx = szIcon.cx; int cy = szIcon.cy; BLENDFUNCTION bf; bf.BlendOp = AC_SRC_OVER; bf.BlendFlags = 0; bf.SourceConstantAlpha = 255; bf.AlphaFormat = 0x01; POINT pt = { 0, 0 }; CClientDC cDC(this); CDC dc; dc.CreateCompatibleDC(&cDC); UINT* pvBits = NULL; HBITMAP hBitmap = CXTPImageManager::Create32BPPDIBSection(cDC, cx, cy, (LPBYTE*)&pvBits); if (pvBits == NULL || hBitmap == NULL) return; MEMCPY_S(pvBits, pBits, XTPToSizeTChecked(cx * cy * 4)); HBITMAP hOld = (HBITMAP)SelectObject(dc, hBitmap); ModifyStyleEx(0, WS_EX_LAYERED); m_pfnUpdateLayeredWindow((HWND)GetSafeHwnd(), (HDC)0, 0, &szIcon, dc.GetSafeHdc(), &pt, 0, &bf, 0x02); SelectObject(dc, hOld); DeleteObject(hBitmap); dc.DeleteDC(); m_bLayered = TRUE; } HRGN CXTPPopupControl::BitmapToRegion(CXTPImageManagerIcon* pIcon) { HRGN hRgn = NULL; if (!pIcon) return NULL; // Create a memory DC inside which we will scan the bitmap content CDC dcMemDC; if (!dcMemDC.CreateCompatibleDC(NULL)) return NULL; int nWidth = pIcon->GetWidth(); int nHeight = pIcon->GetHeight(); LPBYTE lpBits = NULL; HBITMAP hbm32 = CXTPImageManager::Create32BPPDIBSection(dcMemDC, nWidth, nHeight, &lpBits); if (!hbm32 || lpBits == NULL) return NULL; HBITMAP holdBmp = (HBITMAP)SelectObject(dcMemDC, hbm32); dcMemDC.FillSolidRect(0, 0, nWidth, nHeight, 0xFF00FF); pIcon->Draw(&dcMemDC, CPoint(0, 0)); SelectObject(dcMemDC, holdBmp); const DWORD nAlloc = 100; DWORD mMaxRects = nAlloc; RGNDATA* pData = (RGNDATA*)malloc(sizeof(RGNDATAHEADER) + (sizeof(RECT) * mMaxRects)); if (!pData) { DeleteObject(hbm32); return NULL; } pData->rdh.dwSize = sizeof(RGNDATAHEADER); pData->rdh.iType = RDH_RECTANGLES; pData->rdh.nCount = pData->rdh.nRgnSize = 0; SetRect(&pData->rdh.rcBound, 0, 0, nWidth, nHeight); BYTE* p32 = (BYTE*)lpBits + (nHeight - 1) * nWidth * 4; for (int y = 0; y < nHeight; y++) { for (int x = 0; x < nWidth; x++) { int x0 = x; COLORREF* p = (COLORREF*)p32 + x; while (x < nWidth) { if (*p == 0xFF00FF) break; p++; x++; } if (x > x0) { if (pData->rdh.nCount >= mMaxRects) { mMaxRects += nAlloc; RGNDATA* pReData = (RGNDATA*)realloc(pData, sizeof(RGNDATAHEADER) + (sizeof(RECT) * mMaxRects)); if (!pReData) { DeleteObject(hbm32); free(pData); return NULL; } pData = pReData; } RECT* pr = (RECT*)&pData->Buffer; SetRect(&pr[pData->rdh.nCount], x0, y, x, y + 1); pData->rdh.nCount++; if (pData->rdh.nCount > 2000) { HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * mMaxRects), pData); if (hRgn) { CombineRgn(hRgn, hRgn, h, RGN_OR); DeleteObject(h); } else { hRgn = h; } pData->rdh.nCount = 0; } } } p32 -= nWidth * 4; } HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * mMaxRects), pData); if (hRgn) { CombineRgn(hRgn, hRgn, h, RGN_OR); DeleteObject(h); } else { hRgn = h; } free(pData); DeleteObject(hbm32); return hRgn; } void CXTPPopupControl::UpdateBitmapRegion() { if (!GetSafeHwnd()) return; m_bLayered = FALSE; if (m_nBackgroundBitmap <= 0) { SetWindowRgn(NULL, FALSE); return; } CXTPImageManagerIcon* pImage = m_pImageManager->GetImage(XTPToUInt(m_nBackgroundBitmap), 0); if (!pImage) { SetWindowRgn(NULL, FALSE); return; } if (pImage->IsAlpha()) { SetWindowRgn(NULL, FALSE); SetRegionAlphaLayer(pImage); } else { HRGN hRgn = BitmapToRegion(pImage); if (!hRgn) return; SetWindowRgn(hRgn, FALSE); } } BOOL CXTPPopupControl::IsDpiBitmapScalingEnabled() const { return (m_bDpiBitmapScalingModified ? m_bDpiBitmapScaling : XTPDpiHelper()->IsDpiBitmapScalingEnabled()); } void CXTPPopupControl::EnableDpiBitmapScaling(BOOL bEnable /*= TRUE*/) { m_bDpiBitmapScaling = bEnable; m_bDpiBitmapScalingModified = TRUE; } BOOL CXTPPopupControl::Create(CWnd* pParentWnd) { // if hwnd already exist - return TRUE; if (GetSafeHwnd()) return TRUE; // init extended wnd style (for Win98 compatible) DWORD dwExStyle = m_pfnSetLayeredWindowAttributes && (m_nTransparency < 255 || m_popupAnimation == xtpPopupAnimationFade) ? DWORD(WS_EX_LAYERED) : 0; // Create popup Wnd if (!CreateEx(dwExStyle | WS_EX_TOOLWINDOW | WS_EX_TOPMOST | (m_bRightToLeft ? WS_EX_LAYOUTRTL : 0), AfxRegisterWndClass(NULL, AfxGetApp()->LoadStandardCursor(IDC_ARROW)), NULL, WS_POPUP, CRect(0, 0, 0, 0), pParentWnd, NULL)) return FALSE; if (m_pMarkupContext) { XTPMarkupAssignHandle(m_pMarkupContext, m_hWnd); } m_nCurrentTransparency = 255; SetOwner(pParentWnd); UpdateBitmapRegion(); // Set begining state for creating window m_popupState = xtpPopupStateClosed; return TRUE; } void CXTPPopupControl::SetTheme(CXTPPopupPaintManager* pPaintManager) { SAFE_DELETE(m_pPaintManager); // safe delete old theme object // store point to new theme object m_pPaintManager = pPaintManager; m_pPaintManager->RefreshMetrics(); // redraw all controls RedrawControl(); } void CXTPPopupControl::SetTheme(XTPPopupPaintTheme theme) { // set new theme switch (theme) { case xtpPopupThemeMSN: SetTheme(new CXTPPopupThemeMSN()); break; case xtpPopupThemeOffice2003: SetTheme(new CXTPPopupThemeOffice2003()); break; case xtpPopupThemeOffice2013: SetTheme(new CXTPPopupThemeOffice2013()); break; case xtpPopupThemeResource: SetTheme(new CXTPPopupThemeResource()); break; case xtpPopupThemeOfficeXP: SetTheme(new CXTPPopupThemeOfficeXP()); break; case xtpPopupThemeOffice2000: SetTheme(new CXTPPopupThemeOffice2000()); break; case xtpPopupThemeCustom: default: // error case!!! SetTheme(new CXTPPopupPaintManager()); break; } m_paintTheme = theme; } void CXTPPopupControl::RedrawControl() { if (m_hWnd) // call WM_PAINT message Invalidate(FALSE); } CXTPPopupItem* CXTPPopupControl::AddItem(CXTPPopupItem* pItem) { // insert item to item's queue pItem->m_nIndex = (int)m_arrItems.Add(pItem); // init control handler pItem->m_pControl = this; // notify to item about adding inside CXTPPopupControl pItem->OnItemInserted(); return pItem; } void CXTPPopupControl::RemoveAllItems() { // dealocate memory for all items for (int i = 0; i < GetItemCount(); i++) m_arrItems[i]->InternalRelease(); // clear item's array m_arrItems.RemoveAll(); // reset selected and pressed pointers m_pSelected = NULL; m_pPressed = NULL; } void CXTPPopupControl::RemoveItem(CXTPPopupItem* pItem) { _ASSERTE(pItem); CXTPPopupItem* pCurrItem = NULL; // find item pointer in item's queue for (int i = 0; i < GetItemCount(); i++) { pCurrItem = GetItem(i); if (pCurrItem == pItem) { RemoveItem(i); break; } } } void CXTPPopupControl::RemoveItem(int nIndex) { if (nIndex < 0 || nIndex >= GetItemCount()) return; CXTPPopupItem* pCurrItem = m_arrItems[nIndex]; _ASSERTE(pCurrItem); if (!pCurrItem) return; // remove pointer from item's queue m_arrItems.RemoveAt(nIndex); // deallocate memory pCurrItem->InternalRelease(); // if pointer was selected - reset selected pointer if (m_pSelected == pCurrItem) m_pSelected = NULL; // if pointer was pressed - reset pressed pointer if (m_pPressed == pCurrItem) m_pPressed = NULL; // redraw all valid items RedrawControl(); } CXTPPopupItem* CXTPPopupControl::GetItem(int nIndex) const { // return item on an index return m_arrItems[nIndex]; } int CXTPPopupControl::GetItemCount() const { // return count of valid items return (int)m_arrItems.GetSize(); } CXTPPopupItem* CXTPPopupControl::HitTest(CPoint pt) const { // look over a item's pointers in item's queue for (int i = GetItemCount() - 1; i >= 0; i--) { CXTPPopupItem* pItem = GetItem(i); // test item rect to XY location if (pItem->GetRect().PtInRect(pt)) // if OK return pointer return pItem; } return NULL; } BOOL CXTPPopupControl::SetLayeredWindowAttributes(int bAlpha) { if (bAlpha > 255) bAlpha = 255; if (bAlpha == m_nCurrentTransparency) return TRUE; m_nCurrentTransparency = bAlpha; if (m_pfnSetLayeredWindowAttributes && (GetExStyle() & WS_EX_LAYERED)) { if (m_bLayered) { BLENDFUNCTION bf; bf.BlendOp = AC_SRC_OVER; bf.BlendFlags = 0; bf.SourceConstantAlpha = (BYTE)bAlpha; bf.AlphaFormat = 0x01; return m_pfnUpdateLayeredWindow(m_hWnd, (HDC)0, 0, 0, 0, 0, 0, &bf, 0x02); } // if pointer to transparent func - valid return m_pfnSetLayeredWindowAttributes(m_hWnd, 0x00, (BYTE)bAlpha, LWA_ALPHA); } return FALSE; } XTPPopupState CXTPPopupControl::GetPopupState() const { // return current popup state return m_popupState; } void CXTPPopupControl::SetPopupState(XTPPopupState popupState) { if (m_popupState == popupState) return; // set new popup state m_popupState = popupState; // else if CWnd object exist // - notify to parent window about change state Notify(XTP_PCN_STATECHANGED, (LPARAM)this); } void CXTPPopupControl::UpdateState(BOOL /*bInit*/) { // Get current popup wnd rect CRect rc = m_stateCurrent.rcPopup; // set current pos and size SetWindowPos(NULL, rc.left, rc.top, rc.Width(), rc.Height(), SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_SHOWWINDOW); // redraw all items RedrawControl(); // if mouse is capture - window is opaque (return from UpdateState proc) // else, will be need to set current transparent value if (!m_bCapture) SetLayeredWindowAttributes(m_stateCurrent.nTransparency); UpdateWindow(); } BOOL CXTPPopupControl::Close() { // reset capture flag m_bCapture = FALSE; // reset selected and pressed state m_pSelected = m_pPressed = NULL; if (!m_hWnd) return FALSE; EndModalLoop(0); if (m_bAutoDelete) { SetPopupState(xtpPopupStateClosed); return DestroyWindow(); } // destroy m_hWnd object BOOL bResult = DestroyWindow(); // set close state SetPopupState(xtpPopupStateClosed); return bResult; } void CXTPPopupControl::Hide() { // if popup state "Show" - collapsing window if (m_popupState == xtpPopupStateShow) { KillTimer(TID_SHOWDELAY); OnCollapsing(); } } void CXTPPopupControl::OnShow() { // set SHOW state SetPopupState(xtpPopupStateShow); CPoint ptPopup = GetPopupPos(); m_stateCurrent.rcPopup = CRect(CPoint(ptPopup.x - m_szPopup.cx, ptPopup.y - m_szPopup.cy), m_szPopup); m_stateCurrent.nTransparency = m_nTransparency; // start SHOW timer if (m_uShowDelay != (UINT)-1) { SetTimer(TID_SHOWDELAY, m_uShowDelay, NULL); } } void CXTPPopupControl::OnCollapsing() { // set COLLAPSING state SetPopupState(xtpPopupStateCollapsing); // if no-animation mode if (m_popupAnimation == xtpPopupAnimationNone || m_uAnimationDelay <= 0) { // close & destroy popup window Close(); return; } // if Fage animation mode else if (m_popupAnimation == xtpPopupAnimationFade) { // set target transparensy value m_stateTarget.nTransparency = 0; } // if Slide animation mode else if (m_popupAnimation == xtpPopupAnimationSlide) { // set rectangular of target CPoint ptPopup = GetPopupPos(); m_stateTarget.rcPopup = CRect(ptPopup.x - m_szPopup.cx, ptPopup.y, ptPopup.x, ptPopup.y); } // if Unfold animation mode else if (m_popupAnimation == xtpPopupAnimationUnfold) { // set rectangular of target CPoint ptPopup = GetPopupPos(); m_stateTarget.rcPopup = CRect(ptPopup, CSize(0)); } // calc step m_nStep = XTPToIntChecked(max(1, m_uAnimationDelay / m_nAnimationInterval)); // set collapsing timer SetTimer(TID_COLLAPSING, m_nAnimationInterval, NULL); // update view state UpdateState(TRUE); } void CXTPPopupControl::OnExpanding(BOOL bUpdateCurrent) { // set Expanding state SetPopupState(xtpPopupStateExpanding); CPoint ptPopup = GetPopupPos(); // reinit target rect m_stateTarget.rcPopup = CRect(CPoint(ptPopup.x - m_szPopup.cx, ptPopup.y - m_szPopup.cy), m_szPopup); m_stateTarget.nTransparency = m_nTransparency; // if updating flag is set if (bUpdateCurrent) { // reinit curent state object m_stateCurrent = m_stateTarget; // if no-animation mode if (m_popupAnimation == xtpPopupAnimationNone || m_uAnimationDelay <= 0) { // update view UpdateState(TRUE); // show popup OnShow(); // return from proc return; } // if Fage animation mode else if (m_popupAnimation == xtpPopupAnimationFade) { // set target transparensy value m_stateCurrent.nTransparency = 0; } // if Slide animation mode if (m_popupAnimation == xtpPopupAnimationSlide) { // set rectangular of target m_stateCurrent.rcPopup = CRect(ptPopup.x - m_szPopup.cx, ptPopup.y, ptPopup.x, ptPopup.y); } // if Unfold animation mode else if (m_popupAnimation == xtpPopupAnimationUnfold) { // set rectangular of target m_stateCurrent.rcPopup = CRect(ptPopup, CSize(0)); } // calc step m_nStep = XTPToIntChecked(max(1, m_uAnimationDelay / m_nAnimationInterval)); } else { // calc step m_nStep = XTPToIntChecked(max(1, m_uAnimationDelay / m_nAnimationInterval - m_nStep)); } // set expanding timer SetTimer(TID_EXPANDING, m_nAnimationInterval, NULL); // update view state UpdateState(TRUE); } BOOL CXTPPopupControl::Show(CWnd* pParent, int nID /*=0*/) { // create popup wnd if (!Create(pParent)) return FALSE; // check popup state - return if popup state is wrong if (m_popupState != xtpPopupStateClosed) return FALSE; // set the popup id. m_nID = nID; // set expanding OnExpanding(TRUE); return TRUE; } BOOL CXTPPopupControl::ShowModal(CWnd* pParent, int nID /*=0*/) { CWinApp* pApp = AfxGetApp(); if (pApp != NULL) pApp->EnableModeless(FALSE); HWND hWndTop = 0; #if (_MSC_VER <= 1100) CWnd* pParentWnd = CWnd::GetSafeOwner(pParent, &hWndTop); HWND hWndParent = pParentWnd->GetSafeHwnd(); #else HWND hWndParent = CWnd::GetSafeOwner_(pParent->GetSafeHwnd(), &hWndTop); #endif BOOL bEnableParent = FALSE; if (hWndParent != NULL && ::IsWindowEnabled(hWndParent)) { ::EnableWindow(hWndParent, FALSE); bEnableParent = TRUE; } // create popup wnd if (Show(pParent, nID)) { SetFocus(); RunModalLoop(MLF_NOIDLEMSG | MLF_NOKICKIDLE); } if (bEnableParent) ::EnableWindow(hWndParent, TRUE); if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd) ::SetActiveWindow(hWndParent); DestroyWindow(); // re-enable windows if (::IsWindow(hWndTop)) ::EnableWindow(hWndTop, TRUE); hWndTop = NULL; if (pApp != NULL) pApp->EnableModeless(TRUE); return TRUE; } #define MOVETO(A, B, Step) \ if (A != B) \ A += max(1, abs(A - B) / Step) * (A > B ? -1 : 1); void CXTPPopupControl::Animate(int nStep) { // if step == 0 set current state to target state if (nStep < 1) { m_stateCurrent = m_stateTarget; } else { // move MOVETO(m_stateCurrent.rcPopup.top, m_stateTarget.rcPopup.top, nStep); MOVETO(m_stateCurrent.rcPopup.left, m_stateTarget.rcPopup.left, nStep); MOVETO(m_stateCurrent.rcPopup.right, m_stateTarget.rcPopup.right, nStep); MOVETO(m_stateCurrent.rcPopup.bottom, m_stateTarget.rcPopup.bottom, nStep); MOVETO(m_stateCurrent.nTransparency, m_stateTarget.nTransparency, nStep); } // update view state UpdateState(); } #include "Common/Base/Diagnostic/XTPBeginAfxMap.h" BEGIN_MESSAGE_MAP(CXTPPopupControl, CWnd) //{{AFX_MSG_MAP(CXTPPopupControl) ON_WM_ERASEBKGND() ON_WM_PAINT() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_CAPTURECHANGED() ON_WM_MOUSEMOVE() ON_WM_TIMER() ON_WM_SETCURSOR() //}}AFX_MSG_MAP ON_MESSAGE_VOID(WM_MOUSELEAVE, OnMouseLeave) ON_WM_MOUSEACTIVATE() END_MESSAGE_MAP() #include "Common/Base/Diagnostic/XTPEndAfxMap.h" ///////////////////////////////////////////////////////////////////////////// // CXTPPopupControl message handlers int CXTPPopupControl::OnMouseActivate(CWnd* /*pDesktopWnd*/, UINT /*nHitTest*/, UINT /*message*/) { return MA_NOACTIVATE; } void CXTPPopupControl::OnTimer(UINT_PTR nIDEvent) { CWnd::OnTimer(nIDEvent); switch (nIDEvent) { // if expand ore collapsing state case TID_EXPANDING: case TID_COLLAPSING: // animate from current to target Animate(m_nStep); m_nStep--; // if end step if (m_nStep <= 0) { // kill timer event KillTimer(nIDEvent); // change popup state if (nIDEvent == TID_EXPANDING) OnShow(); else Close(); } break; // if popup wnd shown case TID_SHOWDELAY: // if mouse cursor is not capture if (!m_bCapture) { // kill timer event KillTimer(TID_SHOWDELAY); // set collapsing state OnCollapsing(); } break; } } BOOL CXTPPopupControl::OnEraseBkgnd(CDC* /*pDC*/) { return TRUE; } void CXTPPopupControl::OnPaint() { // Get context CDC object CPaintDC dcPaint(this); // init client rect CRect rc(0, 0, m_szPopup.cx, m_szPopup.cy); // init temp buffer CDC object CXTPBufferDC dc(dcPaint, rc); // draw background m_pPaintManager->DrawBackground(&dc, this, rc); // draw all valid items for (int i = 0; i < GetItemCount(); i++) { CXTPPopupItem* pItem = GetItem(i); pItem->Draw(&dc); } } void CXTPPopupControl::Notify(WPARAM wParam, LPARAM lParam) { // get parent CWnd object HWND hWndOwner = m_hWndOwner; if (hWndOwner && ::IsWindow(hWndOwner)) { // send message to parent ::SendMessage(hWndOwner, XTPWM_POPUPCONTROL_NOTIFY, wParam, lParam); } } void CXTPPopupControl::OnClick(CXTPPopupItem* pItem) { if (pItem->GetID() == XTP_ID_POPUP_CLOSE) Close(); else Notify(XTP_PCN_ITEMCLICK, (LPARAM)pItem); } void CXTPPopupControl::TrackMove() { SetCapture(); CPoint ptStart; GetCursorPos(&ptStart); CXTPWindowRect rcStart(this); while (GetCapture() == this) { MSG msg; if (!::GetMessage(&msg, NULL, 0, 0)) { AfxPostQuitMessage((int)msg.wParam); break; } if (msg.message == WM_LBUTTONUP) break; else if (msg.message == WM_MOUSEMOVE) { CPoint pt(msg.pt); CRect rc(rcStart); rc.OffsetRect(pt - ptStart); CRect rcDeskWnd = XTPMultiMonitor()->GetWorkArea(msg.pt); if (rc.left < rcDeskWnd.left) rc.OffsetRect(rcDeskWnd.left - rc.left, 0); if (rc.top < rcDeskWnd.top) rc.OffsetRect(0, rcDeskWnd.top - rc.top); if (rc.right > rcDeskWnd.right) rc.OffsetRect(rcDeskWnd.right - rc.right, 0); if (rc.bottom > rcDeskWnd.bottom) rc.OffsetRect(0, rcDeskWnd.bottom - rc.bottom); MoveWindow(rc); } else if (msg.message == WM_KEYDOWN) { if (msg.wParam == VK_ESCAPE) break; } else DispatchMessage(&msg); } ReleaseCapture(); m_stateTarget.rcPopup = m_stateCurrent.rcPopup = CXTPWindowRect(this); m_ptPopup = m_stateCurrent.rcPopup.BottomRight(); Notify(XTP_PCN_POSCHANGED, (LPARAM)this); } void CXTPPopupControl::OnLButtonDown(UINT nFlags, CPoint point) { if ((GetPopupState() == xtpPopupStateExpanding) && !m_bSplashScreenMode) { m_nStep = 0; Animate(0); // kill timer event KillTimer(TID_EXPANDING); OnShow(); } // test point to pressed controll CXTPPopupItem* pPressed = HitTest(point); if (m_bAllowMove && (!pPressed || (!pPressed->GetID() && !pPressed->IsButton() && (pPressed->GetCaption().IsEmpty() || !pPressed->IsHyperLink())))) { TrackMove(); return; } // if success test if (pPressed) { m_pPressed = pPressed; // set capture SetCapture(); // redraw all valide controls RedrawControl(); } CWnd::OnLButtonDown(nFlags, point); } void CXTPPopupControl::OnLButtonUp(UINT /*nFlags*/, CPoint point) { // if there is pressed control if (m_pPressed) { // store popup pointer CXTPPopupItem* pPressed = m_pPressed; m_pPressed = NULL; // free mouse event ReleaseCapture(); RedrawControl(); // if selected pointer equal pressed pointer - it is clik on item if (pPressed == m_pSelected) { // redraw all valid items OnClick(pPressed); } else { OnMouseMove(0, point); } } } void CXTPPopupControl::OnCaptureChanged(CWnd* pWnd) { // if m_pPressed - reset pointer if (m_pPressed) { m_pPressed = NULL; RedrawControl(); } CWnd::OnCaptureChanged(pWnd); } void CXTPPopupControl::OnMouseMove(UINT nFlags, CPoint point) { if (!m_bSplashScreenMode) { CXTPClientRect rc(this); // test client rect if no-pressed BOOL bInRect = rc.PtInRect(point) || m_pPressed != NULL; // if test successfull and already not capture if (bInRect && !m_bCapture) { // set capture m_bCapture = TRUE; // opaque window SetLayeredWindowAttributes(255); // capture mouse leave event TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd, 0 }; _TrackMouseEvent(&tme); } // else if test fail and there is pressed and selected control if (!bInRect && m_bCapture && m_pPressed == NULL) { // free capture m_bCapture = FALSE; // set current transparent SetLayeredWindowAttributes(m_nTransparency); } // if collapsing state - expand popup window if (m_popupState == xtpPopupStateCollapsing) { // kill collapsing timer KillTimer(TID_COLLAPSING); if (m_popupAnimation == xtpPopupAnimationFade) { OnShow(); } else { OnExpanding(FALSE); } } } // test point to controled items CXTPPopupItem* pSelected = HitTest(point); // if detect new selected item ore lose selection (NULL) if (pSelected != m_pSelected) { // select new item ore set NULL m_pSelected = (m_pPressed == 0 || m_pPressed == pSelected || pSelected == NULL) ? pSelected : NULL; // redraw all items RedrawControl(); } CWnd::OnMouseMove(nFlags, point); } BOOL CXTPPopupControl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { CXTPPopupItem* pItem = HitTest(CXTPClientCursorPos(this)); if (pItem && pItem->IsHyperLink()) { if (m_hHandCursor) { ::SetCursor(m_hHandCursor); return TRUE; } } return CWnd::OnSetCursor(pWnd, nHitTest, message); } void CXTPPopupControl::OnMouseLeave() { // reset mouse vars OnMouseMove(0, CPoint(-1, -1)); } CXTPImageManager* CXTPPopupControl::GetImageManager() const { return m_pImageManager; } void CXTPPopupControl::SetImageManager(CXTPImageManager* pImageManager) { if (m_pImageManager) m_pImageManager->InternalRelease(); m_pImageManager = pImageManager; RedrawControl(); } void CXTPPopupControl::SetLayoutRTL(BOOL bRightToLeft) { if (XTPSystemVersion()->IsLayoutRTLSupported()) { m_bRightToLeft = bRightToLeft; } } void CXTPPopupControl::SetPopupAnimation() { SetPopupAnimation(m_pfnSetLayeredWindowAttributes ? xtpPopupAnimationFade : xtpPopupAnimationSlide); } void CXTPPopupControl::EnableMarkup(BOOL bEnableMarkup) { if (!bEnableMarkup) { XTPMarkupReleaseContext(m_pMarkupContext, TRUE); } else if (!m_pMarkupContext) { m_pMarkupContext = XTPMarkupCreateContext(NULL, TRUE); } } BOOL CXTPPopupControl::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) { if (m_pMarkupContext) { CPoint ptMouse(0); GetCursorPos(&ptMouse); ScreenToClient(&ptMouse); BOOL bRelay = FALSE; CXTPPopupItem* pItem = HitTest(ptMouse); if (pItem && pItem->GetMarkupUIElement()) { bRelay = TRUE; if (XTPMarkupRelayMessage(pItem->GetMarkupUIElement(), message, wParam, lParam, pResult)) return TRUE; } if (!bRelay) { if (XTPMarkupRelayMessage(GetMarkupContext(), message, wParam, lParam, pResult)) return TRUE; } } return CWnd::OnWndMsg(message, wParam, lParam, pResult); }