📄 balloonhelp.cpp
字号:
/*
* Openmysee
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "stdafx.h"
#include "BalloonHelp.h"
#ifndef DFCS_HOT
#define DFCS_HOT 0x1000
#endif
#ifndef WS_EX_LAYERED
#define WS_EX_LAYERED 0x00080000
#define LWA_COLORKEY 0x00000001
#define LWA_ALPHA 0x00000002
#endif
#ifndef CS_DROPSHADOW
#define CS_DROPSHADOW 0x00020000
#endif
#ifndef SPI_GETDROPSHADOW
#define SPI_GETDROPSHADOW 0x1024
#endif
#ifndef SPI_GETTOOLTIPANIMATION
#define SPI_GETTOOLTIPANIMATION 0x1016
#endif
#ifndef SPI_GETTOOLTIPFADE
#define SPI_GETTOOLTIPFADE 0x1018
#endif
/////////////////////////////////////////////////////////////////////////////
// CBalloonHelp
// these could go in resources, if they make you nervous
// if someone knows a better way to deal with timers than to use
// constant IDs, pls let me know?
#define ID_TIMER_SHOW 3333
#define ID_TIMER_HIDE 3334
#define ID_TIMER_CLOSE 3335
#define ID_TIMER_HOTTRACK 3336
// option constants (bits)
const unsigned int CBalloonHelp::unCLOSE_ON_LBUTTON_UP = 0x001;
const unsigned int CBalloonHelp::unCLOSE_ON_RBUTTON_UP = 0x002;
const unsigned int CBalloonHelp::unCLOSE_ON_MOUSE_MOVE = 0x004;
const unsigned int CBalloonHelp::unCLOSE_ON_KEYPRESS = 0x008;
const unsigned int CBalloonHelp::unDELETE_THIS_ON_CLOSE= 0x010;
const unsigned int CBalloonHelp::unSHOW_CLOSE_BUTTON = 0x020;
const unsigned int CBalloonHelp::unSHOW_INNER_SHADOW = 0x040;
const unsigned int CBalloonHelp::unSHOW_TOPMOST = 0x080;
const unsigned int CBalloonHelp::unDISABLE_FADEIN = 0x100;
const unsigned int CBalloonHelp::unDISABLE_FADEOUT = 0x200;
const unsigned int CBalloonHelp::unHIDE_AFTER_CREATED = 0x400;
const unsigned int CBalloonHelp::unDISABLE_FADE = CBalloonHelp::unDISABLE_FADEIN|CBalloonHelp::unDISABLE_FADEOUT;
// layout constants (should prolly be configurable)
const int CBalloonHelp::nTIP_TAIL = 20;
const int CBalloonHelp::nTIP_MARGIN = 8;
// synchronization for keyboard hook
CComCriticalSection CBalloonHelp::s_KeyboardHookSection;
// windows using keyboard hook
CAtlArray<HWND> CBalloonHelp::s_apKeyboardCloseWnds;
// handle to the keyboard hook, if set
HHOOK CBalloonHelp::s_hKeyboardHook = NULL;
void FillSolidRect(HDC hdc, const RECT *pRect, COLORREF clr)
{
SetBkColor(hdc, clr);
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, pRect, NULL, 0, NULL);
}
CBalloonHelp::CBalloonHelp() :
m_fnSetLayeredWindowAttributes(NULL),
m_unOptions(0),
m_unTimeout(0),
m_strURL(""),
m_strContent(""),
m_nAlpha(255),
m_nMouseMoveTolerance(3),
m_uCloseState(0),
m_pTitleFont(NULL),
m_pContentFont(NULL),
m_crForeground(::GetSysColor(COLOR_INFOTEXT)),
m_crBackground(::GetSysColor(COLOR_INFOBK)),
m_ilIcon(NULL),
m_rgnComplete(NULL)
{
s_KeyboardHookSection.Init();
m_ptAnchor.x = m_ptAnchor.y = 0;
m_ptMouseOrig.x = m_ptMouseOrig.y = 0;
// retrieve layered window API if available
HMODULE hUser32 = GetModuleHandle(_T("USER32.DLL"));
m_fnSetLayeredWindowAttributes = (FN_SET_LAYERED_WINDOW_ATTRIBUTES)GetProcAddress(hUser32, _T("SetLayeredWindowAttributes"));
}
CBalloonHelp::~CBalloonHelp()
{
if ( NULL != m_pTitleFont )
DeleteObject(m_pTitleFont);
m_pTitleFont = NULL;
if ( NULL != m_pContentFont )
DeleteObject(m_pContentFont);
m_pContentFont = NULL;
if (m_ilIcon)
ImageList_Destroy(m_ilIcon);
if (m_rgnComplete)
DeleteObject(m_rgnComplete);
}
//
// Show a help balloon on screen
// Parameters:
// strTitle | Title of balloon
// strContent | Content of balloon
// ptAnchor | point tail of balloon will be "anchor"ed to
// szIcon | One of:
// IDI_APPLICATION
// IDI_INFORMATION IDI_ASTERISK (same)
// IDI_ERROR IDI_HAND (same)
// IDI_EXCLAMATION IDI_WARNING (same)
// IDI_QUESTION
// IDI_WINLOGO
// NULL (no icon)
// unOptions | One or more of:
// : unCLOSE_ON_LBUTTON_UP | closes window on WM_LBUTTON_UP
// : unCLOSE_ON_RBUTTON_UP | closes window on WM_RBUTTON_UP
// : unCLOSE_ON_MOUSE_MOVE | closes window when user moves mouse past threshhold
// : unCLOSE_ON_KEYPRESS | closes window on the next keypress message sent to this thread. (!!! probably not thread safe !!!)
// : unSHOW_CLOSE_BUTTON | shows close button in upper right
// : unSHOW_INNER_SHADOW | draw inner shadow in balloon
// : unSHOW_TOPMOST | place balloon above all other windows
// : unDISABLE_FADE | disable the fade-in/fade-out effects (overrides system and user settings)
// : unDISABLE_FADEIN | disable the fade-in effect
// : unDISABLE_FADEOUT | disable the fade-out effect
// pParentWnd | Parent window. If NULL will be set to AfxGetMainWnd()
// strURL | If not empty, when the balloon is clicked ShellExecute() will
// | be called, with strURL passed in.
// unTimeout | If not 0, balloon will automatically close after unTimeout milliseconds.
//
void CBalloonHelp::LaunchBalloon(const CString& strTitle, const CString& strContent,
const CPoint& ptAnchor,
LPCTSTR szIcon, //= IDI_EXCLAMATION
unsigned int unOptions, // unSHOW_CLOSE_BUTTON
HWND pParentWnd ,//= NULL
const CString strURL, //= ""
unsigned int unTimeout)//= 10000
{
CBalloonHelp* pbh = new CBalloonHelp;
if ( NULL != szIcon )
{
HICON hIcon = (HICON)::LoadImage(NULL, szIcon, IMAGE_ICON, 16,16, LR_SHARED);
if (NULL != hIcon)
{
// Use a scaled standard icon (looks very good on Win2k, XP, not so good on Win9x)
CDC dc;
CDC dcTmp1;
CDC dcTmp2;
CBitmap bmpIcon;
CBitmap bmpIconSmall;
dc.Attach(::GetDC(NULL));
dcTmp1.CreateCompatibleDC(&dc);
dcTmp2.CreateCompatibleDC(&dc);
bmpIcon.CreateCompatibleBitmap(&dc, 32,32);
bmpIconSmall.CreateCompatibleBitmap(&dc, 16,16);
::ReleaseDC(NULL, dc.Detach());
// i now have two device contexts and two bitmaps.
// i will select a bitmap in each device context,
// draw the icon into the larger one,
// scale it into the smaller one,
// and set the small one as the balloon icon.
// This is a rather long process to get a small icon,
// but ensures maximum compatibility between different
// versions of Windows, while producing the best possible
// results on each version.
CBitmap* pbmpOld1 = dcTmp1.SelectObject(&bmpIcon);
CBitmap* pbmpOld2 = dcTmp2.SelectObject(&bmpIconSmall);
dcTmp1.FillSolidRect(0,0,32,32, pbh->m_crBackground);
::DrawIconEx(dcTmp1, 0,0,hIcon,32,32,0,NULL,DI_NORMAL);
dcTmp2.SetStretchBltMode(HALFTONE);
dcTmp2.StretchBlt(0,0,16,16,&dcTmp1, 0,0,32,32,SRCCOPY);
dcTmp1.SelectObject(pbmpOld1);
dcTmp2.SelectObject(pbmpOld2);
pbh->SetIcon(bmpIconSmall, pbh->m_crBackground);
}
}
pbh->Create(strTitle, strContent, ptAnchor, unOptions|unDELETE_THIS_ON_CLOSE,
pParentWnd, strURL, unTimeout, NULL);
}
// Sets the font used for drawing the balloon title. Deleted by balloon, do not use CFont* after passing to this function.
void CBalloonHelp::SetTitleFont(HFONT pFont)
{
if ( NULL != m_pTitleFont )
DeleteObject(m_pTitleFont);
m_pTitleFont = pFont;
// if already visible, resize & move
if ( NULL != m_hWnd )
PositionWindow();
}
// Sets the font used for drawing the balloon content. Deleted by balloon, do not use CFont* after passing to this function.
void CBalloonHelp::SetContentFont(HFONT pFont)
{
if ( NULL != m_pContentFont )
DeleteObject(m_pContentFont);
m_pContentFont = pFont;
// if already visible, resize & move
if ( NULL != m_hWnd )
PositionWindow();
}
// Sets the icon displayed in the top left of the balloon (pass NULL to hide icon)
void CBalloonHelp::SetIcon(HICON hIcon)
{
if ( NULL != m_ilIcon )
ImageList_Destroy(m_ilIcon);
ICONINFO iconinfo;
if ( NULL != hIcon && ::GetIconInfo(hIcon, &iconinfo) )
{
SetIcon(iconinfo.hbmColor, iconinfo.hbmMask);
::DeleteObject(iconinfo.hbmColor);
::DeleteObject(iconinfo.hbmMask);
}
// if already visible, resize & move (icon size may have changed)
if ( NULL != m_hWnd )
PositionWindow();
}
// Sets the icon displayed in the top left of the balloon (pass NULL hBitmap to hide icon)
void CBalloonHelp::SetIcon(HBITMAP hBitmap, COLORREF crMask)
{
if ( NULL != m_ilIcon )
ImageList_Destroy(m_ilIcon);
if ( NULL != hBitmap )
{
BITMAP bm;
if (::GetObject(hBitmap, sizeof(bm),(LPVOID)&bm))
{
m_ilIcon = ImageList_Create(bm.bmWidth, bm.bmHeight, ILC_COLOR24|ILC_MASK, 1, 0);
ImageList_AddMasked(m_ilIcon, hBitmap, crMask);
}
}
// if already visible, resize & move (icon size may have changed)
if ( NULL != m_hWnd )
PositionWindow();
}
// Sets the icon displayed in the top left of the balloon
void CBalloonHelp::SetIcon(HBITMAP hBitmap, HBITMAP hMask)
{
if ( NULL != m_ilIcon )
ImageList_Destroy(m_ilIcon);
_ASSERTE(hBitmap);
_ASSERTE(hMask);
BITMAP bm;
if (::GetObject(hBitmap, sizeof(bm),(LPVOID)&bm))
{
m_ilIcon = ImageList_Create(bm.bmWidth, bm.bmHeight, ILC_COLOR24|ILC_MASK, 1, 0);
ImageList_Add(m_ilIcon, hBitmap, hMask);
}
// if already visible, resize & move (icon size may have changed)
if ( NULL != m_hWnd )
PositionWindow();
}
// Set icon displayed in the top left of the balloon to image # nIconIndex from pImageList
void CBalloonHelp::SetIcon(HIMAGELIST pImageList, int nIconIndex)
{
// sanity checks
_ASSERTE(pImageList);
_ASSERTE(nIconIndex >= 0 && nIconIndex < ImageList_GetImageCount(pImageList) );
HICON hIcon = NULL;
if ( NULL != pImageList && nIconIndex >= 0 && nIconIndex < ImageList_GetImageCount(pImageList) )
hIcon = ImageList_ExtractIcon(NULL, pImageList, nIconIndex);
SetIcon(hIcon);
if ( NULL != hIcon )
::DestroyIcon(hIcon);
// if already visible, resize & move (icon size may have changed)
if ( NULL != m_hWnd )
PositionWindow();
}
// Sets the URL to be opened when balloon is clicked. Pass "" to disable.
void CBalloonHelp::SetURL(const CString& strURL)
{
m_strURL = strURL;
}
// Sets the number of milliseconds the balloon can remain open. Set to 0 to disable timeout.
void CBalloonHelp::SetTimeout(unsigned int unTimeout)
{
m_unTimeout = unTimeout;
// if timer is already set, reset.
if ( NULL != m_hWnd )
{
if ( m_unTimeout > 0 )
SetTimer(ID_TIMER_CLOSE, m_unTimeout, NULL);
else
KillTimer(ID_TIMER_CLOSE);
}
}
// Sets the point to which the balloon is "anchored"
void CBalloonHelp::SetAnchorPoint(CPoint ptAnchor)
{
m_ptAnchor = ptAnchor;
// if already visible, move
if ( NULL != m_hWnd )
{
PositionWindow();
}
}
// Sets the title of the balloon
void CBalloonHelp::SetTitle(const CString& strTitle)
{
if(m_strTitle != strTitle)
{
m_strTitle = strTitle;
SetWindowText(m_strTitle);
// if already visible, resize & move
if ( NULL != m_hWnd )
PositionWindow(true);
}
}
// Sets the content of the balloon (plain text only)
void CBalloonHelp::SetContent(const CString& strContent)
{
if(m_strContent != strContent)
{
m_strContent = strContent;
// if already visible, resize & move
if ( NULL != m_hWnd )
PositionWindow(true);
}
}
// Sets the forground (text and border) color of the balloon
void CBalloonHelp::SetForegroundColor(COLORREF crForeground)
{
m_crForeground = crForeground;
// repaint if visible
if ( NULL != m_hWnd )
Invalidate(FALSE);
}
// Sets the background color of the balloon
void CBalloonHelp::SetBackgroundColor(COLORREF crBackground)
{
m_crBackground = crBackground;
// repaint if visible
if ( NULL != m_hWnd )
Invalidate(FALSE);
}
// Sets the distance the mouse must move before the balloon closes when the unCLOSE_ON_MOUSE_MOVE option is set.
void CBalloonHelp::SetMouseMoveTolerance(int nTolerance)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -