Click here to Skip to main content
15,885,546 members
Articles / Desktop Programming / MFC

Balloon Help as a non-modal replacement for MessageBox()

Rate me:
Please Sign up or sign in to vote.
4.98/5 (62 votes)
7 Aug 2002CPOL13 min read 1M   12.3K   228  
Although sometimes useful, message boxes used to display information are often just annoying. This article describes a non-modal replacement.
//
//			CBalloonHelp WTL version
//

// Copyright 2001, Joshua Heyer
// Copyright 2002, Maximilian H�nel (WTL version)
//  You are free to use this code for whatever you want, provided you
// give credit where credit is due.
//  I'm providing this code in the hope that it is useful to someone, as i have
// gotten much use out of other peoples code over the years.
//  If you see value in it, make some improvements, etc., i would appreciate it 
// if you sent me some feedback.

//
// Ported to WTL by Maximilian H�nel (max_haenel@smjh.de) 2002
//
// Revision History:
//
// 28.05.2002:	Version 1.0 (Maximilian H�nel)
//				Initial Version (WTL port)
//
#ifndef __XPBALLOONDLG__H__
#define __XPBALLOONDLG__H__

#pragma once

//
// Don't forget to add BalloonHelp.cpp to your project :-)
//

#ifndef __cplusplus
	#error WTL requires C++ compilation (use a .cpp suffix)
#endif

#ifndef __ATLWIN_H__
	#error BalloonHelp.H requires atlwin.h to be included first
#endif

#ifndef __ATLMISC_H__
	#error BalloonHelp.H requires atlmisc.h to be included first
#endif

#ifndef __ATLCRACK_H__
	#error BalloonHelp.H requires atlcrack.h to be included first
#endif

#ifndef __ATLCTRLS_H__
	#error BalloonHelp.H requires atlctrls.h to be included first
#endif

#ifndef __ATLGDI_H__
	#error BalloonHelp.H requires atlgdi.h to be included first
#endif

#ifndef BALLOON_HELP_NO_NAMESPACE
namespace WTL
{
#endif

////////////////////////////////////////////////////////////////////////////////
// The class _ThunkImpl is a renamed version of Andrew Nosenko CAuxThunk implementation.
// Thanks Andrew, it's a fantastic class!
//
// Copyright (c) 1997-2001 by Andrew Nosenko <andien@nozillium.com>,
// (c) 1997-1998 by Computer Multimedia System, Inc.,
// (c) 1998-2001 by Mead & Co Limited
//
// http://www.nozillium.com/atlaux/
// Version: 1.10.0021
//
#ifndef _M_IX86
	#pragma message("_ThunkImpl/ is implemented for X86 only!")
#endif

#pragma pack(push, 1)

template <class T>
class _ThunkImpl
{
private:

	BYTE	m_mov;			// mov ecx, %pThis
	DWORD	m_this; 		//
	BYTE	m_jmp;			// jmp func
	DWORD	m_relproc;		// relative jmp

protected:
	
	typedef void (T::*TMFP)();
	void InitThunk(TMFP method, const T* pThis)
	{
		union { DWORD func; TMFP method; } addr;
		addr.method = (TMFP)method;
		m_mov  = 0xB9;
		m_this = (DWORD)pThis;
		m_jmp  = 0xE9;
		m_relproc = addr.func - (DWORD)(this+1);

		::FlushInstructionCache(GetCurrentProcess(), this, sizeof(*this));
	}
	FARPROC GetThunk() const {
		_ASSERTE(m_mov == 0xB9);
		return (FARPROC)this; }
};
#pragma pack(pop) // _ThunkImpl


// we need these two dummy classes so we can 
// derive more than once from _ThunkImpl
template <class T>
class _MouseHookThunk: public _ThunkImpl<T> {};

template <class T>
class _KeybHookThunk: public _ThunkImpl<T> {};

template <class T>
class _CallWndRetHookThunk: public _ThunkImpl<T> {};


//
//			class CBalloonAnchor
//
class CBalloonAnchor
{
public:
	
	CBalloonAnchor();

	CPoint GetAnchorPoint();
	
	void SetAnchorPoint(const CPoint& pt);
	void SetAnchorPoint(HWND hWndCenter);
	void SetFollowMe(HWND hWndFollow, POINT* pptAnchor);
	BOOL IsFollowMeMode(){return m_wndFollow.IsWindow();}
	BOOL AnchorPointChanged();
		
protected:

	CPoint	m_ptAnchor;
	CSize	m_sizeOffset;
	CWindow	m_wndFollow;

};// class CBalloonAnchor


//
//			class CBalloonHelp (WTL version)
//
class CBalloonHelp:
	public CWindowImpl<CBalloonHelp>,
	public _KeybHookThunk<CBalloonHelp>,
	public _MouseHookThunk<CBalloonHelp>,
	public _CallWndRetHookThunk<CBalloonHelp>
{
private:
	
	typedef CBalloonHelp thisClass;
	typedef CWindowImpl<CBalloonHelp>		WindowBase;
	typedef _KeybHookThunk<thisClass>		KeybHook;
	typedef _MouseHookThunk<thisClass>		MouseHook;
	typedef _CallWndRetHookThunk<thisClass>	CallWndRetHook;

public:

	DECLARE_WND_CLASS_EX(_T("XPBalloonHelp"),CS_HREDRAW|CS_VREDRAW|CS_SAVEBITS|CS_DBLCLKS,COLOR_WINDOW)

	CBalloonHelp();
	~CBalloonHelp();

	enum BallonOptions{
		BOShowCloseButton		= 0x0001,	// Shows close button in upper right.
		BOShowInnerShadow		= 0x0002,	// Draw inner shadow in balloon.
		BOShowTopMost			= 0x0004,	// Place balloon above all other windows.

		BOCloseOnLButtonDown	= 0x0008,	// Closes window on WM_LBUTTONDOWN.
		BOCloseOnMButtonDown	= 0x0010,	// Closes window on WM_MBUTTONDOWN.
		BOCloseOnRButtonDown	= 0x0020,	// Closes window on WM_RBUTTONDOWN.
		BOCloseOnButtonDown		= BOCloseOnLButtonDown|BOCloseOnMButtonDown|BOCloseOnRButtonDown,

		BOCloseOnLButtonUp		= 0x0040,	// Closes window on WM_LBUTTONUP.
		BOCloseOnMButtonUp		= 0x0080,	// Closes window on WM_MBUTTONUP.
		BOCloseOnRButtonUp		= 0x0100,	// Closes window on WM_RBUTTONUP.
		BOCloseOnButtonUp		= BOCloseOnLButtonUp|BOCloseOnMButtonUp|BOCloseOnRButtonUp,

		BOCloseOnMouseMove		= 0x0200,	// Closes window when user moves mouse past threshhold.
		BOCloseOnKeyDown		= 0x0400,	// Closes window on the next keypress message sent to this thread.
		BOCloseOnAppDeactivate	= 0x0800,	// Closes window when the application is being deactivated.
		
		BODeleteThisOnClose		= 0x1000,	// Deletes object when window is closed.  Use only if the object is created on the heap.

		BODisableFadeIn			= 0x2000,	// Disable the fade-in effect (overrides system and user settings).
		BODisableFadeOut		= 0x4000,	// Disable the fade-out effect (overrides system and user settings).
		BODisableFade			= BODisableFadeIn|BODisableFadeOut,

		BONoShow				= 0x8000,	// Dont't show balloon after creating. Usefull if you want to animate the balloon.

		BODefault=BODisableFadeOut|BOCloseOnButtonDown|BOCloseOnKeyDown|BOCloseOnAppDeactivate,
	};

	
	static BOOL Show(HWND hWndOwner, UINT nIdTitle, UINT nIdContent,
				HWND hWndAnchor, LPCTSTR pszIcon=IDI_EXCLAMATION, DWORD dwOptions=BODefault, 
				LPCTSTR pszURL=NULL, UINT uTimeout=0);

	static BOOL Show(HWND hWndOwner, LPCTSTR pszTitle, LPCTSTR pszContent,
				HWND hWndAnchor, LPCTSTR pszIcon=IDI_EXCLAMATION, DWORD dwOptions=BODefault,
				LPCTSTR pszURL=NULL, UINT uTimeout=0);

	static BOOL Show(HWND hWndOwner, UINT nIdTitle, UINT nIdContent, 
				const CPoint& ptAnchor, LPCTSTR pszIcon=IDI_EXCLAMATION, DWORD dwOptions=BODefault,
				LPCTSTR pszURL=NULL, UINT uTimeout=0);

	static BOOL Show(HWND hWndOwner, LPCTSTR pszTitle, LPCTSTR pszContent,
				const CPoint& ptAnchor, LPCTSTR pszIcon=IDI_EXCLAMATION, DWORD dwOptions=BODefault,
				LPCTSTR pszURL=NULL, UINT uTimeout=0);
	
	BOOL Create(HWND hWndOwner, LPCTSTR pszTitle, 
				LPCTSTR pszContent, const POINT* pptAnchor=NULL, DWORD dwOptions=BODefault,  
				LPCTSTR pszURL=NULL, UINT uTimeout=0);

	enum{OleSysColorBits=0x80000000};

	void SetTitleFont(HFONT hFont);
	void SetContentFont(HFONT hFont);
	void SetIcon(LPCTSTR pszIcon);
	void SetIcon(HICON hIcon);
	void SetIcon(HBITMAP hBitmap, COLORREF crMask);
	void SetIcon(HBITMAP hBitmap, HBITMAP hMask);
	void SetIcon(HIMAGELIST hImgList, int nIconIndex);
	void SetTimeout(UINT nTimeout);
	void SetURL(LPCTSTR pszUrl);
	void SetAnchorPoint(CPoint ptAnchor);
	void SetAnchorPoint(HWND hWndAnchor);
	void SetFollowMe(HWND hWndFollow, POINT* pptAnchor);
	void SetTitle(LPCTSTR pszTitle);
	void SetContent(LPCTSTR pszContent);
	void SetForeGroundColor(OLE_COLOR clr);
	void SetBackgroundColor(OLE_COLOR clr);
	void SetMouseMoveTolerance(int nTolerance);

	inline
	static COLORREF OleTranslateColor(OLE_COLOR ole_clr)
	{
		COLORREF clr;
		::OleTranslateColor(ole_clr,NULL,&clr);
		return clr;
	}

protected:
	
	enum BalloonQuadrant{BQTopRight, BQTopLeft, BQBottomLeft, BQBottomRight};
	BalloonQuadrant GetBalloonQuadrant();
	void GetAnchorScreenBounds(CRect& rcBounds);

	enum{nTipMargin=8, nTipTail=20, nCXCloseBtn=14,nCYCloseBtn=14,nAnimDuration=200};
	CSize DrawHeader(HDC hdc, bool bDraw=true);
	CSize DrawContent(HDC hdc, int nTop, bool bDraw=true);
	CSize CalcHeaderSize(HDC hdc){return DrawHeader(hdc,false);}
	CSize CalcContentSize(HDC hdc){return DrawContent(hdc,0,false);}
	CSize CalcClientSize();
	CSize CalcWindowSize();
	
	void DrawBkgnd(HDC hdc);
	void Draw(HDC hdc);
	void DrawNc(HDC hdc, BOOL bExcludeClient=TRUE);

	void PositionWindow();
	void ShowWindow();
	void CloseWindow();
	void Animate(BOOL bShow);
	
	enum {IdTimerClose=1,IdTimerHotTrack=2,TimerHotTrackElapse=100};
	BEGIN_MSG_MAP_EX(thisClass)
		MSG_WM_DESTROY(OnDestroy)
		MSG_WM_ERASEBKGND(OnEraseBkgnd)
		MSG_WM_PAINT(OnPaint)
		MSG_WM_NCPAINT(OnNcPaint)
		MSG_WM_NCCALCSIZE(OnNcCalcSize)
		MSG_WM_CAPTURECHANGED(OnCaptureChanged)
		MSG_WM_MOUSEMOVE(OnMouseMove)
		MSG_WM_LBUTTONDOWN(OnLButtonDown)
		MSG_WM_LBUTTONUP(OnLButtonUp)
		MSG_WM_MOUSEACTIVATE(OnMouseActivate)
		MSG_WM_TIMER(OnTimer)
		MSG_WM_ACTIVATEAPP(OnActivateApp)
		MSG_WM_PRINT(OnPrint)
	END_MSG_MAP()

protected:
	
	void OnDestroy();
	bool OnEraseBkgnd(HDC){return true;}
	void OnPaint(HDC hdc);
	void OnNcPaint(HRGN);
	LRESULT OnNcCalcSize(BOOL bCalcValidRects, LPARAM lParam);
	void OnCaptureChanged(HWND);
	void OnMouseMove(UINT, CPoint pt);
	void OnLButtonDown(UINT, CPoint pt);
	void OnLButtonUp(UINT, CPoint pt);
	int	 OnMouseActivate(HWND hWnd, UINT nHitTest,UINT nMsg){return MA_NOACTIVATE;}
#if (_WTL_VER < 0x0700)
	// This is the bogus version of WTL 3.x
	void OnTimer(UINT nIDEvent, TIMERPROC*);
#else
	// This is the correct version. Fixed in WTL 7.0
	void OnTimer(UINT nIDEvent, TIMERPROC);
#endif 
	void OnActivateApp(BOOL bActivate, DWORD dwTask);
	void OnFinalMessage(HWND);
	void OnPrint(HDC hdc,UINT);

	//
	//			Hook related functions
	//

	//			WH_KEYBOARD
	void SetKeyboardHook();
	void RemoveKeyboardHook();
	HHOOK GetKeybHookHandle(){return m_hKeybHook;}
	
	LRESULT KeyboardHookProc(int code, WPARAM wParam, LPARAM lParam);

	//			WH_MOUSE
	void SetMouseHook();
	void RemoveMouseHook();
	HHOOK GetMouseHookHandle(){return m_hMouseHook;}
	
	LRESULT MouseHookProc(int code, WPARAM wParam, LPARAM lParam);
	
	//			WH_CALLWNDPROCRET
	void SetCallWndRetHook();
	void RemoveCallWndRetHook();
	HHOOK GetCallWndRetHandle(){return m_hCallWndRetHook;}
	
	LRESULT CallWndRetProc(int code, WPARAM wParam, LPARAM lParam);
	
private:
	
	HHOOK m_hKeybHook;
	HHOOK m_hMouseHook;
	HHOOK m_hCallWndRetHook;
	
protected:
	
	DWORD			m_dwOptions;
	UINT			m_uTimeout;
	INT				m_nMouseMoveTolerance;

	CFont			m_TitleFont;
	CFont			m_ContentFont;
	
	OLE_COLOR		m_clrForeground;
	OLE_COLOR		m_clrBackground;
	
	CString			m_strContent;
	CString			m_strURL;
	CBalloonAnchor	m_Anchor;
	CRect			m_rcScreen;

	CImageList		m_ilIcon;
	
	CPoint			m_ptMouseOrg;
	UINT			m_uCloseState;
	CRgn			m_rgnComplete;

};// class CBalloonHelp (WTL version)

#ifndef BALLOON_HELP_NO_NAMESPACE
}// namespace WTL
#endif

#endif // #ifndef __XPBALLOONDLG__H__

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
Poke...

Comments and Discussions