464 lines
12 KiB
C++
464 lines
12 KiB
C++
#include "stdafx.h"
|
|
#include "CaptionButton.h"
|
|
#include "globalFunc.h"
|
|
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[]=__FILE__;
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
CCaptionButton::CCaptionButton(const TCHAR* sTip,
|
|
UINT ID_Bmp_up, UINT ID_Bmp_down,
|
|
BOOL bCheckBtn)
|
|
{
|
|
m_pParent=NULL;
|
|
m_sTip=sTip;
|
|
m_idBmpUp = ID_Bmp_up;
|
|
m_idBmpDown = ID_Bmp_down;
|
|
m_bCheckBtn = bCheckBtn;
|
|
|
|
m_bEnable = TRUE;
|
|
m_bBtnChecked = FALSE;
|
|
m_bHighLight = FALSE;
|
|
m_rc = CRect(0,0,0,0);
|
|
m_uiBtnCount = 0;
|
|
m_CAdj = new COLORADJUSTMENT;
|
|
m_TME.cbSize = sizeof(TRACKMOUSEEVENT);
|
|
m_TME.dwFlags = TME_LEAVE;
|
|
m_TME.dwHoverTime = HOVER_DEFAULT;
|
|
}
|
|
CCaptionButton::~CCaptionButton()
|
|
{
|
|
delete m_CAdj;
|
|
}
|
|
|
|
// announce the user defined message:
|
|
DECLARE_USER_MESSAGE(WMU_HITCAPBUTTON)
|
|
|
|
// point = the position where the hit occured
|
|
// bHover = check if mouse was only hovering(TRUE) or clicked(FALSE)
|
|
// return value = button hovered/clicked
|
|
BOOL CCaptionButton::ButtonHitTest(CPoint point, BOOL bHover)
|
|
{
|
|
if(m_pParent==NULL)
|
|
return FALSE;
|
|
|
|
if(m_bEnable)
|
|
{
|
|
CPoint hitPoint;
|
|
::ScreenToClient(GetHWnd(), &point);
|
|
hitPoint.x = point.x;
|
|
hitPoint.y = point.y *(-1); // *(-1) because the point.y has coordinates of
|
|
// the client area (i.e. -12) and we need the point in the NC area (i.e. 12)
|
|
|
|
if(m_rc.PtInRect(hitPoint))
|
|
{
|
|
if(bHover)
|
|
{
|
|
if(!m_bHighLight)
|
|
{
|
|
m_bHighLight = TRUE;
|
|
::RedrawWindow(GetHWnd(), m_rc, NULL, RDW_INVALIDATE | RDW_FRAME);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_bHighLight = TRUE;
|
|
if(m_bCheckBtn)
|
|
m_bBtnChecked ? m_bBtnChecked = FALSE : m_bBtnChecked = TRUE;
|
|
|
|
::RedrawWindow(GetHWnd(), m_rc, NULL, RDW_INVALIDATE | RDW_FRAME);
|
|
::SendMessage(GetHWnd(), WMU_HITCAPBUTTON , (WPARAM)m_uiBtnCount, (LPARAM)m_bBtnChecked);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if(m_bHighLight)
|
|
{
|
|
m_bHighLight = FALSE;
|
|
//::RedrawWindow(m_hWndCap, m_rc,NULL, RDW_INVALIDATE | RDW_FRAME|RDW_ERASE);//不能正确erase局部!
|
|
::RedrawWindow(GetHWnd(), NULL,NULL, RDW_INVALIDATE | RDW_FRAME|RDW_NOCHILDREN);
|
|
CRect rect;
|
|
::GetClientRect(GetHWnd(),rect);
|
|
::ValidateRect(GetHWnd(),rect);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#define ICON_SIZE 13
|
|
|
|
// uiBtnCount = the no. of the button to paint
|
|
// return value = button checked TRUE/FALSE
|
|
BOOL CCaptionButton::PaintBmpCheckBtn()
|
|
{
|
|
if(m_pParent==NULL)
|
|
return FALSE;
|
|
|
|
UINT BmpID = 0;
|
|
CSize sizeButton(0,0);
|
|
CDC dcMemo;
|
|
CWnd *pWnd=CWnd::FromHandle(GetHWnd());
|
|
if(pWnd==NULL)
|
|
return FALSE;
|
|
CDC *dc =CWnd::FromHandle(GetHWnd())->GetWindowDC();
|
|
if(dc==NULL)
|
|
return FALSE;
|
|
|
|
// calculate the caption rectangle:
|
|
CalcCapRect();
|
|
CRect rect(m_rc);
|
|
GlobalFunc::Exchange(rect.top,rect.bottom);
|
|
rect.top*=-1;
|
|
rect.bottom*=-1;
|
|
m_pParent->m_ctrlTip.SetToolRect(CWnd::FromHandle(GetHWnd()),m_uiBtnCount,rect);
|
|
|
|
CRect iconRect=m_rc;
|
|
if(iconRect.Width()>ICON_SIZE)
|
|
{
|
|
iconRect.left+=(iconRect.Width()-ICON_SIZE)/2;
|
|
iconRect.right=iconRect.left +ICON_SIZE;
|
|
}
|
|
if(iconRect.Height()>ICON_SIZE)
|
|
{
|
|
iconRect.top+=(iconRect.Height()-ICON_SIZE)/2;
|
|
iconRect.bottom=iconRect.top +ICON_SIZE;
|
|
}
|
|
|
|
// find state and load bitmap from the resource:
|
|
if(m_bCheckBtn)
|
|
m_bBtnChecked ? BmpID = m_idBmpDown : BmpID = m_idBmpUp;
|
|
else
|
|
m_bHighLight ? BmpID = m_idBmpDown : BmpID = m_idBmpUp;
|
|
|
|
if(m_bHighLight)
|
|
{
|
|
CBrush brush(RGB(120,195,240));
|
|
//CBrush brush(RGB(202,236,255));
|
|
CBrush* pOldBrush=dc->SelectObject(&brush);
|
|
dc->Rectangle(m_rc);
|
|
dc->SelectObject(pOldBrush);
|
|
}
|
|
|
|
HGDIOBJ hOldBitmap;
|
|
HBITMAP hBmp;
|
|
{
|
|
CTemporaryResourceOverride vTemp;
|
|
|
|
// load the bitmap into the handle:
|
|
hBmp= (HBITMAP)::LoadImage(AfxGetResourceHandle(),
|
|
MAKEINTRESOURCE(BmpID),
|
|
IMAGE_BITMAP,
|
|
ICON_SIZE,
|
|
ICON_SIZE,
|
|
LR_LOADMAP3DCOLORS);
|
|
dcMemo.CreateCompatibleDC(dc);
|
|
hOldBitmap=dcMemo.SelectObject(hBmp);
|
|
}
|
|
|
|
if(!m_bEnable)
|
|
{ // set the stretch-draw-mode to HALFTONE:
|
|
dc->SetStretchBltMode(HALFTONE);
|
|
// get the current COLORADJUSTMET struct:
|
|
dc->GetColorAdjustment(m_CAdj);
|
|
// change some values of the struct:
|
|
m_CAdj->caColorfulness = -80;
|
|
m_CAdj->caBrightness = -25;
|
|
// set the changed struct:
|
|
dc->SetColorAdjustment(m_CAdj);
|
|
}
|
|
|
|
// blow the bitmap to the titlebar:
|
|
OSVERSIONINFO osvi;
|
|
ZeroMemory( &osvi, sizeof( osvi ) );
|
|
osvi.dwOSVersionInfoSize = sizeof( osvi );
|
|
GetVersionEx( &osvi );
|
|
if(osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)//9X
|
|
{
|
|
dc->StretchBlt( iconRect.left, iconRect.top,
|
|
ICON_SIZE, ICON_SIZE,
|
|
&dcMemo,
|
|
0, 0,
|
|
ICON_SIZE, ICON_SIZE,
|
|
SRCCOPY);
|
|
}
|
|
else
|
|
::TransparentBlt(dc->m_hDC,iconRect.left,iconRect.top,ICON_SIZE,ICON_SIZE,dcMemo.m_hDC,
|
|
0,0,ICON_SIZE,ICON_SIZE,GetSysColor(COLOR_3DFACE));
|
|
|
|
dcMemo.SelectObject(hOldBitmap);
|
|
DeleteObject(hBmp);
|
|
|
|
if(m_bHighLight)
|
|
dc->Draw3dRect(m_rc,RGB(0, 0, 0),RGB(0, 0, 0));
|
|
|
|
// clean up:
|
|
::ReleaseDC(GetHWnd() ,dcMemo.m_hDC);
|
|
::ReleaseDC(GetHWnd() ,dc->m_hDC);
|
|
|
|
return m_bBtnChecked;
|
|
}
|
|
|
|
// pCapRect = pointer to the caption rectangle
|
|
void CCaptionButton::CalcCapRect()
|
|
{
|
|
CRect *pCapRect=&m_rc;
|
|
if(m_pParent==NULL)
|
|
return;
|
|
|
|
CSize sizeButton(0,0);
|
|
CSize sizeCaption(0,0);
|
|
|
|
// Get the needed system metrics:
|
|
CSize sizeFrame (GetSystemMetrics(SM_CXFRAME), GetSystemMetrics(SM_CYFRAME));
|
|
#ifndef _WIN64
|
|
if(GetWindowLong(GetHWnd(), GWL_EXSTYLE) & WS_EX_TOOLWINDOW)
|
|
#else
|
|
if(GetWindowLongPtr(GetHWnd(), GWL_EXSTYLE) & WS_EX_TOOLWINDOW)
|
|
#endif
|
|
{
|
|
sizeButton.cy = sizeButton.cx = GetSystemMetrics(SM_CYSMSIZE);
|
|
sizeCaption.cy= GetSystemMetrics(SM_CYSMCAPTION);
|
|
}
|
|
else
|
|
{
|
|
sizeButton.cy = sizeButton.cx = GetSystemMetrics(SM_CYSIZE);
|
|
sizeCaption.cy= GetSystemMetrics(SM_CYCAPTION);
|
|
}
|
|
|
|
// Get the window rectangle and window style:
|
|
CRect rcWindow;
|
|
CWnd::FromHandle(GetHWnd())->GetWindowRect(rcWindow);
|
|
|
|
DWORD dwStyle = CWnd::FromHandle(GetHWnd())->GetStyle();
|
|
|
|
// Set the rectangle based on the system metrics:
|
|
//if (dwStyle & WS_THICKFRAME)
|
|
//{
|
|
// pCapRect->left = 0;
|
|
// pCapRect->top = ((sizeCaption.cy+sizeFrame.cy)-sizeButton.cy);
|
|
// // assume there are already 3 buttons
|
|
// pCapRect->right = rcWindow.Width()-(((1+m_uiBtnCount)*sizeButton.cx))-3;
|
|
|
|
//}
|
|
if (dwStyle & WS_MINIMIZEBOX||dwStyle & WS_MAXIMIZEBOX)
|
|
{
|
|
pCapRect->left = 0;
|
|
pCapRect->top = ((sizeCaption.cy+sizeFrame.cy)-sizeButton.cy);
|
|
// assume there are already 3 buttons
|
|
pCapRect->right = rcWindow.Width()-(((2+m_uiBtnCount)*sizeButton.cx))-3;
|
|
}
|
|
else if (dwStyle & WS_SYSMENU)
|
|
{
|
|
pCapRect->left = 0;
|
|
pCapRect->top = ((sizeCaption.cy+sizeFrame.cy)-sizeButton.cy);
|
|
// assume there is only 1 button
|
|
pCapRect->right = rcWindow.Width()-(((m_uiBtnCount+1)*sizeButton.cx))-2;
|
|
}
|
|
else if (dwStyle & WS_CAPTION)
|
|
{
|
|
pCapRect->left = 0;
|
|
pCapRect->top = ((sizeCaption.cy+sizeFrame.cy)-sizeButton.cy);
|
|
// assume there is no button
|
|
// gjm modify 100811,解决“专业词库”对话框,在WIN7和VISTA操作系统下,“X”符号必须点击在对话框之外才起作用的BUG
|
|
// pCapRect->right = rcWindow.Width()-(m_uiBtnCount*sizeButton.cx)-2;
|
|
pCapRect->right = rcWindow.Width()-(m_uiBtnCount*sizeButton.cx)-7;
|
|
}
|
|
|
|
pCapRect->bottom = ((pCapRect->top - sizeFrame.cy)) + sizeButton.cy;
|
|
|
|
pCapRect->right -=3;
|
|
pCapRect->left = pCapRect->right - sizeButton.cx+2;
|
|
}
|
|
|
|
// return value = previous button state
|
|
BOOL CCaptionButton::EnableButton(BOOL bEnable)
|
|
{
|
|
if(m_pParent==NULL)
|
|
return FALSE;
|
|
|
|
if(m_bEnable == bEnable) return m_bEnable;
|
|
|
|
BOOL bOld = m_bEnable;
|
|
m_bEnable = bEnable;
|
|
::RedrawWindow(GetHWnd(), m_rc, NULL, RDW_INVALIDATE | RDW_FRAME);
|
|
return bOld;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL CCapToolTip::AddToolTip( UINT nID, LPRECT lpRect, LPCTSTR lpszText )
|
|
{
|
|
TOOLINFO Tips;
|
|
memset(&Tips, 0, sizeof(TOOLINFO));
|
|
Tips.cbSize = sizeof(TOOLINFO);
|
|
Tips.hwnd = GetOwner()->GetSafeHwnd();
|
|
Tips.uFlags = TTF_SUBCLASS;
|
|
Tips.uId = nID;
|
|
memcpy(&Tips.rect, lpRect, sizeof(RECT));
|
|
Tips.lpszText = (LPTSTR)lpszText;
|
|
return (BOOL)SendMessage(TTM_ADDTOOL, 0, (LPARAM)&Tips);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
DualPtrList<CCapBtnWnd*> g_lstCapWnd;//全局CCapBtnWnd
|
|
|
|
CCapBtnWnd::CCapBtnWnd():m_hWndCap(NULL),m_pWndProcOld(NULL)
|
|
{
|
|
g_lstCapWnd.AddTail(this);
|
|
}
|
|
CCapBtnWnd::~CCapBtnWnd()
|
|
{
|
|
g_lstCapWnd.FindAndDel(this);
|
|
}
|
|
LRESULT CALLBACK HookedWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CCapBtnWnd *pBtnWnd=NULL;
|
|
for(g_lstCapWnd.MoveToFirst();!g_lstCapWnd.IsOut();g_lstCapWnd.MoveToNext())
|
|
{
|
|
if(g_lstCapWnd.GetCurData()->GetHWnd()==hWnd)
|
|
{
|
|
pBtnWnd=g_lstCapWnd.GetCurData();
|
|
break;
|
|
}
|
|
}
|
|
if(pBtnWnd==NULL)
|
|
return 0;
|
|
|
|
return pBtnWnd->HookProc(message,wParam,lParam);
|
|
}
|
|
void CCapBtnWnd::Hook(HWND hWnd)
|
|
{
|
|
m_hWndCap=hWnd;
|
|
if(m_hWndCap!=NULL)
|
|
{
|
|
#ifdef _WIN64
|
|
m_pWndProcOld = (WNDPROC)::SetWindowLongPtr(hWnd , GWLP_WNDPROC, (DWORD_PTR)HookedWindowProc);
|
|
#else
|
|
m_pWndProcOld = (WNDPROC)::SetWindowLong(hWnd , GWL_WNDPROC, (DWORD)HookedWindowProc);
|
|
#endif
|
|
if(IsHooked())
|
|
m_ctrlTip.Create(CWnd::FromHandle(m_hWndCap),TTS_ALWAYSTIP);
|
|
}
|
|
|
|
}
|
|
void CCapBtnWnd::UnHook()
|
|
{
|
|
if(IsHooked())
|
|
{
|
|
#ifdef _WIN64
|
|
SetWindowLongPtr(GetHWnd() , GWLP_WNDPROC, (DWORD_PTR)m_pWndProcOld);
|
|
#else
|
|
SetWindowLong(GetHWnd() , GWL_WNDPROC, (DWORD)m_pWndProcOld);
|
|
#endif
|
|
m_pWndProcOld=NULL;
|
|
m_hWndCap=NULL;
|
|
}
|
|
}
|
|
void CCapBtnWnd::AddCapBtn(CCaptionButton*pBtn)
|
|
{
|
|
pBtn->SetParent(this);
|
|
pBtn->SetIndex(m_lstBtn.GetLength());
|
|
pBtn->CalcCapRect();
|
|
pBtn->m_TME.hwndTrack = m_hWndCap;
|
|
|
|
CRect rect(pBtn->m_rc);
|
|
GlobalFunc::Exchange(rect.top,rect.bottom);
|
|
rect.top*=-1;
|
|
rect.bottom*=-1;
|
|
m_ctrlTip.AddToolTip(pBtn->GetIndex(),rect,pBtn->m_sTip);
|
|
|
|
m_lstBtn.AddTail(pBtn);
|
|
}
|
|
BOOL CCapBtnWnd::IsWindowActive(BOOL &bActive)
|
|
{
|
|
// Mimic MFC kludge to stay active if WF_STAYACTIVE bit is on
|
|
if (CWnd::FromHandle(m_hWndCap)->m_nFlags & WF_STAYACTIVE)
|
|
bActive = TRUE;
|
|
|
|
if (!CWnd::FromHandle(m_hWndCap)->IsWindowEnabled()) // but not if disabled
|
|
bActive = FALSE;
|
|
|
|
if (bActive == m_bActive)
|
|
return TRUE; // nothing to do
|
|
|
|
m_bActive = bActive; // update the state of the Bitmaps
|
|
|
|
// do not call the base class because it will call Default()
|
|
// and we may have changed bActive.
|
|
return ::DefWindowProc(m_hWndCap, WM_NCACTIVATE, bActive, 0L);
|
|
}
|
|
|
|
LRESULT CCapBtnWnd::HookProc(UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_NCACTIVATE:
|
|
{
|
|
// intercept WM_NCACTIVATE and call the static function isWindowActive!
|
|
// do not call the base class because it will call Default()
|
|
// and we may have changed bActive.
|
|
// get object for this buttons from the map:
|
|
BOOL b = IsWindowActive((BOOL &)wParam);
|
|
// Now it's time to repaint the button(s):
|
|
// get object for this buttons from the map:
|
|
for(m_lstBtn.MoveToFirst();!m_lstBtn.IsOut();m_lstBtn.MoveToNext())
|
|
m_lstBtn.GetCurData()->PaintBmpCheckBtn();
|
|
return b;
|
|
}
|
|
break;
|
|
case WM_NCLBUTTONDOWN:
|
|
case WM_NCLBUTTONDBLCLK:
|
|
{
|
|
for(m_lstBtn.MoveToFirst();!m_lstBtn.IsOut();m_lstBtn.MoveToNext())
|
|
m_lstBtn.GetCurData()->ButtonHitTest((CPoint)lParam);
|
|
break;
|
|
}
|
|
case WM_NCRBUTTONDOWN:
|
|
case WM_NCRBUTTONUP:
|
|
case WM_NCRBUTTONDBLCLK:
|
|
case WM_NCMBUTTONDOWN:
|
|
case WM_NCMBUTTONUP:
|
|
case WM_NCMBUTTONDBLCLK:
|
|
return 0;
|
|
case WM_NCMOUSEMOVE:
|
|
{
|
|
for(m_lstBtn.MoveToFirst();!m_lstBtn.IsOut();m_lstBtn.MoveToNext())
|
|
{
|
|
if(m_lstBtn.GetCurData()->ButtonHitTest((CPoint)lParam, TRUE))
|
|
return 0;
|
|
else
|
|
_TrackMouseEvent(&(m_lstBtn.GetCurData()->m_TME));
|
|
}
|
|
}
|
|
break;
|
|
#if(WINVER >= 0x0500)
|
|
case WM_NCMOUSELEAVE:
|
|
{
|
|
for(m_lstBtn.MoveToFirst();!m_lstBtn.IsOut();m_lstBtn.MoveToNext())
|
|
{
|
|
m_lstBtn.GetCurData()->ButtonHitTest((CPoint)lParam, TRUE);
|
|
_TrackMouseEvent(&(m_lstBtn.GetCurData()->m_TME));
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
case WM_NCPAINT:
|
|
{
|
|
LRESULT nRet=::CallWindowProc(m_pWndProcOld, GetHWnd(), message, wParam, lParam);
|
|
// get object for this buttons from the map:
|
|
for(m_lstBtn.MoveToFirst();!m_lstBtn.IsOut();m_lstBtn.MoveToNext())
|
|
m_lstBtn.GetCurData()->PaintBmpCheckBtn();
|
|
return nRet;
|
|
}
|
|
}
|
|
|
|
// if the message is not handled yet then return to the original window proc:
|
|
return ::CallWindowProc(m_pWndProcOld, m_hWndCap, message, wParam, lParam);
|
|
}
|
|
|
|
|
|
|
|
|
|
|