#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 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); }