1299 lines
27 KiB
C++

// XTPPopupControl.cpp: implementation of the CXTPPopupControl class.
//
// This file is a part of the XTREME CONTROLS MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Common/Resource.h"
#include "Common/XTPDrawHelpers.h"
#include "Common/XTPImageManager.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPVC80Helpers.h"
#include "Common/XTPMarkupRender.h"
#include "Common/XTPResourceManager.h"
#include "Common/XTPColorManager.h"
#include "../Defines.h"
#include "XTPPopupControl.h"
#include "XTPPopupItem.h"
#include "XTPPopupPaintManager.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_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 = 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 = (PFNSETLAYEREDWINDOWATTRIBUTES) ::GetProcAddress(hLib, "SetLayeredWindowAttributes");
m_pfnUpdateLayeredWindow = (LPFNUPDATELAYEREDWINDOW) 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);
}
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);
}
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 (!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, 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(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::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) ? 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)
{
//delete old theme object
delete m_pPaintManager;
//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 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)
{
ASSERT(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];
ASSERT(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 = 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 = max(1, m_uAnimationDelay/m_nAnimationInterval);
}
else
{
//calc step
m_nStep = 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)
{
//create popup wnd
if (!Create(pParent))
return FALSE;
//check popup state - return if popup state is wrong
if (m_popupState != xtpPopupStateClosed)
return FALSE;
//set expanding
OnExpanding(TRUE);
return TRUE;
}
BOOL CXTPPopupControl::ShowModal(CWnd* pParent)
{
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))
{
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();
}
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()
/////////////////////////////////////////////////////////////////////////////
// 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);
}
else if (!m_pMarkupContext)
{
m_pMarkupContext = XTPMarkupCreateContext(0);
}
}
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);
}