📄 balloonhelp.cpp
字号:
DeleteObject(region);
DeleteObject(regionRound);
// There is a bug with layered windows and NC changes in Win2k
// As a workaround, redraw the entire window if the NC area changed.
// Changing the anchor point is the ONLY thing that will change the
// position of the client area relative to the window during normal
// operation.
if ( bRegionChanged || forceRedraw)
RedrawWindow(NULL, NULL, RDW_UPDATENOW| RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
}
// Displays the balloon on the screen, performing fade-in if enabled.
void CBalloonHelp::ShowBalloon(void)
{
if(!IsWindowVisible())
{
ShowWindow(SW_SHOWNOACTIVATE);
m_nAlpha = 5;
SetLayeredWindowAttributes(0, m_nAlpha, LWA_ALPHA);
UINT_PTR i = SetTimer(ID_TIMER_SHOW, 25, NULL);
}
}
// Removes the balloon from the screen, performing the fade-out if enabled
void CBalloonHelp::HideBalloon(void)
{
if(IsWindowVisible())
{
if(m_nAlpha > 250)
m_nAlpha = 250;
UINT_PTR i = SetTimer(ID_TIMER_HIDE, 25, NULL);
}
}
// Removes the balloon from the screen immediately
void CBalloonHelp::HideBalloonImmediately(void)
{
if(IsWindowVisible())
{
KillTimer(ID_TIMER_SHOW);
ShowWindow(SW_HIDE);
}
}
// Sets up the keyboard hook; adds this to list to close
void CBalloonHelp::SetKeyboardHook()
{
s_KeyboardHookSection.Lock();
// if hook is not yet set, set
if ( NULL == s_hKeyboardHook )
{
s_hKeyboardHook = ::SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)KeyboardProc, NULL, ::GetCurrentThreadId());
}
s_apKeyboardCloseWnds.Add(m_hWnd);
s_KeyboardHookSection.Unlock();
}
// Removes this from the list of windows to close; removes hook if not in use.
void CBalloonHelp::RemoveKeyboardHook()
{
s_KeyboardHookSection.Lock();
INT_PTR i;
for (i=0; i < (int)s_apKeyboardCloseWnds.GetCount(); ++i)
{
if ( s_apKeyboardCloseWnds.GetAt(i) == m_hWnd )
s_apKeyboardCloseWnds.RemoveAt(i--);
}
// if no more windows need to be notified, get rid of hook
if ( s_apKeyboardCloseWnds.GetCount() == 0 && NULL != s_hKeyboardHook )
{
::UnhookWindowsHookEx(s_hKeyboardHook);
s_hKeyboardHook = NULL;
}
s_KeyboardHookSection.Unlock();
}
// Erase client area of balloon
LRESULT CBalloonHelp::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
HDC hdc = (HDC)wParam;
CRect rect;
GetClientRect(&rect);
FillSolidRect(hdc, &rect, m_crBackground);
return 0;
}
// draw balloon client area (title & contents)
LRESULT CBalloonHelp::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
PAINTSTRUCT ps;
BeginPaint(&ps);
CSize sizeHeader = DrawHeader(ps.hdc);
DrawContent(ps.hdc, sizeHeader.cy + nTIP_MARGIN);
EndPaint(&ps);
return 0;
}
// draw balloon shape & boarder
LRESULT CBalloonHelp::OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
HDC hdc = GetWindowDC();
// Paint into this DC
CRect rect;
GetWindowRect(&rect);
ScreenToClient(&rect);
CRect rectClient;
GetClientRect(&rectClient);
rectClient.OffsetRect(-rect.left, -rect.top);
rect.OffsetRect(-rect.left, -rect.top);
ExcludeClipRect(hdc, rectClient.left, rectClient.top, rectClient.right, rectClient.bottom);
FillSolidRect(hdc, &rect, m_crBackground);
_ASSERTE(m_rgnComplete);
CBrush brushFg;
brushFg.CreateSolidBrush(m_crForeground);
if ( m_unOptions & unSHOW_INNER_SHADOW )
{
CBrush brushHL;
// slightly lighter color
int red = 170 + GetRValue(m_crBackground)/3;
int green = 170 + GetGValue(m_crBackground)/3;
int blue = 170 + GetBValue(m_crBackground)/3;
brushHL.CreateSolidBrush(RGB(red,green,blue));
OffsetRgn(m_rgnComplete, 1, 1);
FrameRgn(hdc, m_rgnComplete, brushHL, 2, 2);
// slightly darker color
red = GetRValue(m_crForeground)/3 + GetRValue(m_crBackground)/3*2;
green = GetGValue(m_crForeground)/3 + GetGValue(m_crBackground)/3*2;
blue = GetBValue(m_crForeground)/3 + GetBValue(m_crBackground)/3*2;
brushHL.DeleteObject();
OffsetRgn(m_rgnComplete, -2, -2);
brushHL.CreateSolidBrush(RGB(red,green,blue));
FrameRgn(hdc, m_rgnComplete, brushHL, 2, 2);
OffsetRgn(m_rgnComplete, 1, 1);
}
// outline
FrameRgn(hdc, m_rgnComplete, brushFg, 1, 1);
ReleaseDC(hdc);
return 0;
}
// Close button handler
LRESULT CBalloonHelp::OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CPoint point(lParam);
if (m_unOptions & unSHOW_CLOSE_BUTTON)
{
CRect rect;
GetClientRect(&rect);
rect.left = rect.right-16;
rect.bottom = rect.top+16;
if ( rect.PtInRect(point) )
{
m_uCloseState |= DFCS_PUSHED;
SetCapture();
OnMouseMove(uMsg, wParam, lParam, bHandled);
}
}
return 0;
}
// Close button handler,
// close on LButton up handler
LRESULT CBalloonHelp::OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CPoint point(lParam);
if ( (m_unOptions & unSHOW_CLOSE_BUTTON) && (m_uCloseState & DFCS_PUSHED) )
{
ReleaseCapture();
m_uCloseState &= ~DFCS_PUSHED;
CRect rect;
GetClientRect(&rect);
rect.left = rect.right-16;
rect.bottom = rect.top+16;
if ( rect.PtInRect(point) )
HideBalloon();
}
else if ( m_unOptions & unCLOSE_ON_LBUTTON_UP )
{
ReleaseCapture();
HideBalloon();
}
else if ( !m_strURL.IsEmpty() )
{
HideBalloon();
::ShellExecute(NULL, NULL, m_strURL, NULL, NULL, SW_SHOWNORMAL);
}
return 0;
}
// Close on RButton up checking
LRESULT CBalloonHelp::OnRButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CPoint point(lParam);
if ( m_unOptions & unCLOSE_ON_RBUTTON_UP )
{
ReleaseCapture();
HideBalloon();
}
return 0;
}
//
// do mouse tracking:
// Close on mouse move;
// Tracking for close button;
//
LRESULT CBalloonHelp::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CPoint point(lParam);
if (m_unOptions & unSHOW_CLOSE_BUTTON)
{
CRect rect;
GetClientRect(&rect);
rect.left = rect.right-16;
rect.bottom = rect.top+16;
UINT uState = DFCS_CAPTIONCLOSE | DFCS_FLAT;
BOOL bPushed = m_uCloseState&DFCS_PUSHED;
m_uCloseState &= ~DFCS_PUSHED;
if ( rect.PtInRect(point) )
{
uState |= DFCS_HOT;
if ( bPushed )
uState |= DFCS_PUSHED;
SetTimer(ID_TIMER_HOTTRACK, 500, NULL);
}
if ( uState != m_uCloseState )
{
CDC dc;
dc.Attach(GetDC());
dc.DrawFrameControl(&rect, DFC_CAPTION, uState);
m_uCloseState = uState;
dc.Detach();
}
if ( bPushed )
m_uCloseState |= DFCS_PUSHED;
}
if ( m_unOptions & unCLOSE_ON_MOUSE_MOVE )
{
if ( m_nAlpha == 255 && (abs(point.x-m_ptMouseOrig.x) > m_nMouseMoveTolerance || abs(point.y-m_ptMouseOrig.y) > m_nMouseMoveTolerance) )
HideBalloon();
else
SetTimer(ID_TIMER_HOTTRACK, 100, NULL);
}
return 0;
}
// Ensures client area is the correct size relative to window size,
// presearves client contents if possible when moving.
LRESULT CBalloonHelp::OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
BOOL bCalcValidRects = (BOOL)wParam;
NCCALCSIZE_PARAMS FAR* lpncsp = (LPNCCALCSIZE_PARAMS)lParam;
// nTIP_MARGIN pixel margin on all sides
::InflateRect(&lpncsp->rgrc[0], -nTIP_MARGIN,-nTIP_MARGIN);
// nTIP_TAIL pixel "tail" on side closest to anchor
switch ( GetBalloonQuadrant() )
{
case BQ_TOPRIGHT:
case BQ_TOPLEFT:
lpncsp->rgrc[0].top += nTIP_TAIL;
break;
case BQ_BOTTOMRIGHT:
case BQ_BOTTOMLEFT:
lpncsp->rgrc[0].bottom -= nTIP_TAIL;
break;
}
// sanity: ensure rect does not have negative size
if ( lpncsp->rgrc[0].right < lpncsp->rgrc[0].left )
lpncsp->rgrc[0].right = lpncsp->rgrc[0].left;
if ( lpncsp->rgrc[0].bottom < lpncsp->rgrc[0].top )
lpncsp->rgrc[0].bottom = lpncsp->rgrc[0].top;
if ( bCalcValidRects )
{
// determine if client position has changed relative to the window position
// if so, don't bother presearving anything.
if ( !::EqualRect(&lpncsp->rgrc[0], &lpncsp->rgrc[2]) )
{
::SetRectEmpty(&lpncsp->rgrc[2]);
}
}
return 0;
}
// handler for various timers
LRESULT CBalloonHelp::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
UINT nIDEvent = (UINT)wParam;
switch (nIDEvent)
{
case ID_TIMER_SHOW:
m_nAlpha += 25;
if ( m_nAlpha > 255 || m_unOptions&unDISABLE_FADEIN)
{
m_nAlpha = 255;
KillTimer(ID_TIMER_SHOW);
SetTimeout(m_unTimeout); // start close timer
}
else
SetLayeredWindowAttributes(0, m_nAlpha, LWA_ALPHA);
break;
case ID_TIMER_HIDE:
// just in case...
KillTimer(ID_TIMER_SHOW);
m_nAlpha -= 25;
if ( m_nAlpha < 0 || m_unOptions&unDISABLE_FADEOUT )
{
m_nAlpha = 0;
KillTimer(ID_TIMER_HIDE);
ShowWindow(SW_HIDE);
// DestroyWindow();
}
else
{
SetLayeredWindowAttributes(0, m_nAlpha, LWA_ALPHA);
}
break;
case ID_TIMER_CLOSE:
KillTimer(ID_TIMER_CLOSE);
HideBalloon();
break;
case ID_TIMER_HOTTRACK:
{
CPoint point;
KillTimer(ID_TIMER_HOTTRACK);
::GetCursorPos(&point);
ScreenToClient(&point);
OnMouseMove(uMsg, wParam, MAKEWORD(point.x, point.y), bHandled);
break;
}
}
return 0;
}
// Called as the window is being destroyed. Completes destruction after removing keyboard hook.
LRESULT CBalloonHelp::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// remove from list, if set
RemoveKeyboardHook();
return 0;
}
// close the balloon, performing any set transition effect.
LRESULT CBalloonHelp::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
HideBalloon();
return 0;
}
// Keyboard hook; used to implement the unCLOSE_ON_KEYPRESS option
LRESULT CALLBACK CBalloonHelp::KeyboardProc( int code, WPARAM wParam, LPARAM lParam)
{
// Skip if the key was released or if it's a repeat
if (code >= 0 && !(lParam & 0x80000000))
{
s_KeyboardHookSection.Lock();
INT_PTR nNumWnds = CBalloonHelp::s_apKeyboardCloseWnds.GetCount();
INT_PTR i;
for (i=0; i<nNumWnds; ++i)
::PostMessage((HWND)CBalloonHelp::s_apKeyboardCloseWnds.GetAt(i), WM_CLOSE, 0, 0);
CBalloonHelp::s_apKeyboardCloseWnds.RemoveAll();
s_KeyboardHookSection.Unlock();
}
return ::CallNextHookEx(CBalloonHelp::s_hKeyboardHook, code, wParam, lParam);
}
// Called after window has been destroyed. Destroys the object if option is set.
void CBalloonHelp::OnFinalMessage(HWND)
{
// free object if requested
// be careful with this one :D
if ( m_unOptions & unDELETE_THIS_ON_CLOSE )
delete this;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -