OSDN Git Service

Git Clone Dialog Basic Okay
[tortoisegit/TortoiseGitJp.git] / src / Utils / MiscUI / Balloon.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseSVN\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 #include "stdafx.h"\r
20 #include "Balloon.h"\r
21 \r
22 tagBALLOON_INFO::tagBALLOON_INFO()\r
23     : hIcon(NULL),                      \r
24           sBalloonTip(),\r
25           nMask(0),\r
26           nStyles(0),\r
27           nDirection(0),\r
28           nEffect(0),\r
29           nBehaviour(0),\r
30           crBegin(0),\r
31           crMid(0),\r
32           crEnd(0)\r
33 {\r
34 }\r
35 \r
36 CBalloon::CBalloon()\r
37     : m_nStyles (0)\r
38 {\r
39         m_pParentWnd = NULL;\r
40         m_hCurrentWnd = NULL;\r
41         m_hDisplayedWnd = NULL;\r
42 \r
43         m_rgnShadow.CreateRectRgn(0, 0, 1, 1);\r
44         m_rgnBalloon.CreateRectRgn(0, 0, 1, 1);\r
45 \r
46         m_ptOriginal.x = -1;\r
47         m_ptOriginal.y = -1;\r
48 \r
49         SetDelayTime(TTDT_INITIAL, 500);\r
50         SetDelayTime(TTDT_AUTOPOP, 30000);\r
51         SetNotify(FALSE);\r
52         SetDirection();\r
53         SetBehaviour();\r
54         SetDefaultStyles();\r
55         SetDefaultColors();\r
56         SetDefaultSizes();\r
57         SetEffectBk(BALLOON_EFFECT_SOLID);\r
58         RemoveAllTools();\r
59         m_bButtonPushed = FALSE;\r
60 \r
61         // Register the window class if it has not already been registered.\r
62         WNDCLASS wndcls;\r
63         HINSTANCE hInst = AfxGetInstanceHandle();\r
64         if(!(::GetClassInfo(hInst, BALLOON_CLASSNAME, &wndcls)))\r
65         {\r
66                 // otherwise we need to register a new class\r
67                 wndcls.style                    = CS_SAVEBITS;\r
68                 wndcls.lpfnWndProc              = ::DefWindowProc;\r
69                 wndcls.cbClsExtra               = wndcls.cbWndExtra = 0;\r
70                 wndcls.hInstance                = hInst;\r
71                 wndcls.hIcon                    = NULL;\r
72                 wndcls.hCursor                  = LoadCursor(hInst, IDC_ARROW );\r
73                 wndcls.hbrBackground    = NULL;\r
74                 wndcls.lpszMenuName             = NULL;\r
75                 wndcls.lpszClassName    = BALLOON_CLASSNAME;\r
76 \r
77                 if (!AfxRegisterClass(&wndcls))\r
78                         AfxThrowResourceException();\r
79         }\r
80 }\r
81 \r
82 CBalloon::~CBalloon()\r
83 {\r
84         RemoveAllTools();\r
85 \r
86         m_rgnBalloon.DeleteObject();\r
87         m_rgnShadow.DeleteObject();\r
88 \r
89         if (IsWindow(m_hWnd))\r
90         DestroyWindow();\r
91 }\r
92 \r
93 \r
94 BEGIN_MESSAGE_MAP(CBalloon, CWnd)\r
95         //{{AFX_MSG_MAP(CBalloon)\r
96         ON_WM_PAINT()\r
97         ON_WM_TIMER()\r
98         ON_WM_DESTROY()\r
99         ON_WM_KILLFOCUS()\r
100         //}}AFX_MSG_MAP\r
101         ON_WM_MOUSEMOVE()\r
102         ON_WM_LBUTTONDOWN()\r
103         ON_WM_LBUTTONUP()\r
104 END_MESSAGE_MAP()\r
105 \r
106 \r
107 /////////////////////////////////////////////////////////////////////////////\r
108 // CBalloon message handlers\r
109 \r
110 BOOL CBalloon::Create(CWnd* pParentWnd) \r
111 {\r
112         DWORD dwStyle = WS_POPUP; \r
113         DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST;\r
114 \r
115         m_pParentWnd = pParentWnd;\r
116 \r
117         if (!CreateEx(dwExStyle, BALLOON_CLASSNAME, NULL, dwStyle, 0, 0, 0, 0, m_pParentWnd->GetSafeHwnd(), NULL, NULL))\r
118         {\r
119                 return FALSE;\r
120         }\r
121         SetDefaultFont();\r
122         \r
123         return TRUE;\r
124 }\r
125 \r
126 void CBalloon::OnDestroy() \r
127 {\r
128         TRACE("OnDestroy()\n");\r
129         KillTimers();\r
130         \r
131         CWnd::OnDestroy();\r
132 }\r
133 \r
134 \r
135 \r
136 void CBalloon::OnKillFocus(CWnd* pNewWnd) \r
137 {\r
138         CWnd::OnKillFocus(pNewWnd);\r
139         Pop();\r
140 }\r
141 \r
142 \r
143 BOOL CBalloon::PreTranslateMessage(MSG* pMsg) \r
144 {\r
145         RelayEvent(pMsg);\r
146 \r
147         return CWnd::PreTranslateMessage(pMsg);\r
148 }\r
149 \r
150 LRESULT CBalloon::SendNotify(CWnd * pWnd, CPoint * pt, BALLOON_INFO & bi)\r
151 {\r
152         //make sure this is a valid window\r
153         if (!IsWindow(GetSafeHwnd()))\r
154                 return 0L;\r
155 \r
156         //see if the user wants to be notified\r
157         if (!GetNotify())\r
158                 return 0L;\r
159 \r
160         NM_BALLOON_DISPLAY lpnm;\r
161         \r
162         lpnm.pWnd                 = pWnd;\r
163         lpnm.pt                   = pt;\r
164         lpnm.bi                   = &bi;\r
165         lpnm.hdr.hwndFrom = m_hWnd;\r
166     lpnm.hdr.idFrom   = GetDlgCtrlID();\r
167     lpnm.hdr.code     = UDM_TOOLTIP_DISPLAY;\r
168         \r
169         ::SendMessage(m_hNotifyWnd, WM_NOTIFY, lpnm.hdr.idFrom, (LPARAM)&lpnm);\r
170 \r
171         return 0L;\r
172 }\r
173 \r
174 void CBalloon::OnPaint() \r
175 {\r
176         //if (!m_pCurrentWnd)\r
177         //      return;\r
178 \r
179         m_hDisplayedWnd = m_hCurrentWnd;\r
180 \r
181         CPaintDC dc(this); // device context for painting\r
182 \r
183         CRect rect;\r
184         GetClientRect(&rect);\r
185         rect.DeflateRect(0, 0, 1, 1);\r
186 \r
187         //create a memory device-context. This is done to help reduce\r
188         //screen flicker, since we will paint the entire control to the\r
189         //off screen device context first.CDC memDC;\r
190         CDC memDC;\r
191         CBitmap bitmap;\r
192         memDC.CreateCompatibleDC(&dc);\r
193         bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());\r
194         CBitmap* pOldBitmap = memDC.SelectObject(&bitmap); \r
195         \r
196         memDC.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &dc, 0, 0, SRCCOPY);\r
197         \r
198         //draw the tooltip\r
199         OnDraw(&memDC, rect);\r
200 \r
201         //Copy the memory device context back into the original DC.\r
202         dc.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &memDC, 0,0, SRCCOPY);\r
203         \r
204         //Cleanup resources.\r
205         memDC.SelectObject(pOldBitmap);\r
206         memDC.DeleteDC();\r
207         bitmap.DeleteObject(); \r
208 }\r
209 \r
210 void CBalloon::OnDraw(CDC * pDC, CRect rect)\r
211 {\r
212         CBrush brBackground(m_crColor [BALLOON_COLOR_BK_BEGIN]);\r
213         CBrush brShadow(m_crColor [BALLOON_COLOR_SHADOW]);\r
214         CBrush brBorder(m_crColor [BALLOON_COLOR_BORDER]);\r
215         \r
216         pDC->SetBkMode(TRANSPARENT); \r
217         pDC->SetTextColor(m_crColor [BALLOON_COLOR_FG]);\r
218         //set clip region of the tooltip and draw the shadow if needed\r
219         if (m_pToolInfo.nStyles & BALLOON_SHADOW)\r
220         {\r
221                 //draw the shadow for the tooltip\r
222                 int nRop2Mode = pDC->SetROP2(R2_MASKPEN);\r
223                 pDC->FillRgn(&m_rgnShadow, &brShadow);\r
224                 pDC->SetROP2(nRop2Mode);\r
225                 rect.DeflateRect(0, 0, m_nSizes[XBLSZ_SHADOW_CX], m_nSizes[XBLSZ_SHADOW_CY]);\r
226         }\r
227         pDC->SelectClipRgn(&m_rgnBalloon);\r
228 \r
229         OnDrawBackground(pDC, &rect);\r
230 \r
231         //draw the main region's border of the tooltip\r
232         pDC->FrameRgn(&m_rgnBalloon, &brBorder, m_nSizes[XBLSZ_BORDER_CX], m_nSizes[XBLSZ_BORDER_CY]);\r
233 \r
234         if ((m_nLastDirection == BALLOON_RIGHT_BOTTOM) || (m_nLastDirection == BALLOON_LEFT_BOTTOM))\r
235                 rect.top += m_nSizes[XBLSZ_HEIGHT_ANCHOR];\r
236         else\r
237                 rect.bottom -= m_nSizes[XBLSZ_HEIGHT_ANCHOR];\r
238 \r
239         if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON)\r
240         {\r
241                 m_rtCloseButton = CRect(\r
242                         rect.right - m_szCloseButton.cx - m_nSizes[XBLSZ_BUTTON_MARGIN_CX] , rect.top + m_nSizes[XBLSZ_BUTTON_MARGIN_CY], \r
243                         rect.right - m_nSizes[XBLSZ_BUTTON_MARGIN_CX], rect.top + m_szCloseButton.cy + m_nSizes[XBLSZ_BUTTON_MARGIN_CY]);\r
244                 pDC->DrawFrameControl(m_rtCloseButton, DFC_CAPTION, DFCS_CAPTIONCLOSE|DFCS_FLAT|DFCS_TRANSPARENT);\r
245                 rect.right -= (m_szCloseButton.cx + m_nSizes[XBLSZ_BUTTON_MARGIN_CX]);\r
246         }\r
247 \r
248         //get the rectangle to draw the tooltip text\r
249         rect.DeflateRect(m_nSizes[XBLSZ_MARGIN_CX], m_nSizes[XBLSZ_MARGIN_CY]);\r
250 \r
251         //draw the icon\r
252         if (m_pToolInfo.hIcon != NULL)\r
253         {\r
254                 DrawIconEx(pDC->m_hDC, m_nSizes[XBLSZ_MARGIN_CX], rect.top + (rect.Height() - m_szBalloonIcon.cy) / 2, \r
255                         m_pToolInfo.hIcon, m_szBalloonIcon.cx, m_szBalloonIcon.cy, 0, NULL, DI_NORMAL);\r
256 \r
257                 rect.left += m_szBalloonIcon.cx + m_nSizes[XBLSZ_MARGIN_CX]; \r
258         }\r
259 \r
260 \r
261         //aligns tool tip's text\r
262         if (m_pToolInfo.nStyles & BALLOON_BOTTOM_ALIGN)\r
263                 rect.top = rect.bottom - m_szBalloonText.cy;\r
264         else if (m_pToolInfo.nStyles & BALLOON_VCENTER_ALIGN)\r
265                 rect.top += (rect.Height() - m_szBalloonText.cy) / 2;\r
266 \r
267         //prints the tool tip's text\r
268         DrawHTML(pDC, rect, m_pToolInfo.sBalloonTip, m_LogFont, FALSE);\r
269 \r
270         //free resources\r
271         brBackground.DeleteObject();\r
272         brShadow.DeleteObject();\r
273         brBorder.DeleteObject();\r
274 }\r
275 \r
276 void CBalloon::OnDrawBackground(CDC * pDC, CRect * pRect)\r
277 {\r
278 #ifdef USE_GDI_GRADIENT\r
279         #define DRAW CGradient::DrawGDI\r
280 #else\r
281         #define DRAW CGradient::Draw\r
282 #endif\r
283         switch (m_pToolInfo.nEffect)\r
284         {\r
285         case BALLOON_EFFECT_HGRADIENT:\r
286                 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END]);\r
287                 break;\r
288         case BALLOON_EFFECT_VGRADIENT:\r
289                 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END], FALSE);\r
290                 break;\r
291         case BALLOON_EFFECT_HCGRADIENT:\r
292                 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END], m_crColor[BALLOON_COLOR_BK_BEGIN]);\r
293                 break;\r
294         case BALLOON_EFFECT_VCGRADIENT:\r
295                 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END], m_crColor[BALLOON_COLOR_BK_BEGIN], FALSE);\r
296                 break;\r
297         case BALLOON_EFFECT_3HGRADIENT:\r
298                 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_MID], m_crColor[BALLOON_COLOR_BK_END]);\r
299                 break;\r
300         case BALLOON_EFFECT_3VGRADIENT:\r
301                 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_MID], m_crColor[BALLOON_COLOR_BK_END], FALSE);\r
302                 break;\r
303 #undef DRAW\r
304         default:\r
305                 pDC->FillSolidRect(pRect, m_crColor[BALLOON_COLOR_BK_BEGIN]);\r
306                 break;\r
307         }\r
308 }\r
309 \r
310 CRect CBalloon::GetWindowRegion(CRgn * rgn, CSize sz, CPoint pt) const\r
311 {\r
312         CRect rect;\r
313         rect.SetRect(0, 0, sz.cx, sz.cy);\r
314         CRgn rgnRect;\r
315         CRgn rgnAnchor;\r
316         CPoint ptAnchor [3];\r
317         ptAnchor [0] = pt;\r
318         ScreenToClient(&ptAnchor [0]);\r
319 \r
320         switch (m_nLastDirection)\r
321         {\r
322         case BALLOON_LEFT_TOP:\r
323         case BALLOON_RIGHT_TOP:\r
324                 rect.bottom -= m_nSizes[XBLSZ_HEIGHT_ANCHOR];\r
325                 ptAnchor [1].y = ptAnchor [2].y = rect.bottom;\r
326                 break;\r
327         case BALLOON_LEFT_BOTTOM:\r
328         case BALLOON_RIGHT_BOTTOM:\r
329                 rect.top += m_nSizes[XBLSZ_HEIGHT_ANCHOR];\r
330                 ptAnchor [1].y = ptAnchor [2].y = rect.top;\r
331                 break;\r
332         }\r
333 \r
334         //get the region for rectangle with the text\r
335         if (m_pToolInfo.nStyles & BALLOON_ROUNDED)\r
336                 rgnRect.CreateRoundRectRgn(rect.left, rect.top, rect.right + 1, rect.bottom + 1, \r
337                         m_nSizes[XBLSZ_ROUNDED_CX], m_nSizes[XBLSZ_ROUNDED_CY]);\r
338         else rgnRect.CreateRectRgn(rect.left, rect.top, rect.right + 1, rect.bottom + 1);\r
339 \r
340         //gets the region for the anchor\r
341         if (m_pToolInfo.nStyles & BALLOON_ANCHOR)\r
342         {\r
343                 switch (m_nLastDirection)\r
344                 {\r
345                 case BALLOON_LEFT_TOP:\r
346                 case BALLOON_LEFT_BOTTOM:\r
347                         ptAnchor [1].x = rect.right - m_nSizes[XBLSZ_MARGIN_ANCHOR];\r
348                         ptAnchor [2].x = ptAnchor [1].x - m_nSizes[XBLSZ_WIDTH_ANCHOR];\r
349                         break;\r
350                 case BALLOON_RIGHT_TOP:\r
351                 case BALLOON_RIGHT_BOTTOM:\r
352                         ptAnchor [1].x = rect.left + m_nSizes[XBLSZ_MARGIN_ANCHOR];\r
353                         ptAnchor [2].x = ptAnchor [1].x + m_nSizes[XBLSZ_WIDTH_ANCHOR];\r
354                         break;\r
355                 }\r
356                 rgnAnchor.CreatePolygonRgn(ptAnchor, 3, ALTERNATE);\r
357         }\r
358         else\r
359                 rgnAnchor.CreateRectRgn(0, 0, 0, 0);\r
360         \r
361         rgn->CreateRectRgn(0, 0, 1, 1);\r
362         rgn->CombineRgn(&rgnRect, &rgnAnchor, RGN_OR);\r
363         \r
364         rgnAnchor.DeleteObject();\r
365         rgnRect.DeleteObject();\r
366 \r
367         return rect;\r
368 }\r
369 \r
370 void CBalloon::RelayEvent(MSG* pMsg)\r
371 {\r
372         HWND hWnd = NULL;\r
373         CPoint pt;\r
374         CString str;\r
375         CRect rect;\r
376 \r
377         BALLOON_INFO  Info;\r
378                 \r
379         switch(pMsg->message)\r
380         {\r
381         case WM_MOUSEMOVE:\r
382                 if (m_ptOriginal == pMsg->pt)\r
383                         return;\r
384 \r
385                 m_ptOriginal = pMsg->pt; \r
386                 \r
387                 //get the real window under the mouse pointer\r
388                 pt = pMsg->pt;\r
389                 if (m_pParentWnd)\r
390                         m_pParentWnd->ScreenToClient(&pt);\r
391                 hWnd = GetChildWindowFromPoint(pt);\r
392 \r
393                 if (!hWnd)\r
394                 {\r
395                         if (!(GetBehaviour() & BALLOON_DIALOG))\r
396                         {\r
397                                 Pop();\r
398                                 m_hCurrentWnd = NULL;\r
399                                 m_hDisplayedWnd = NULL;\r
400                                 return;\r
401                         }\r
402                 }\r
403                 else\r
404                 {\r
405                         UINT behaviour = GetBehaviour(CWnd::FromHandle(hWnd));\r
406                         if (hWnd == m_hDisplayedWnd)\r
407                         {\r
408                                 if (IsWindowVisible())\r
409                                 {\r
410                                         if ((behaviour & BALLOON_TRACK_MOUSE)&&(!(behaviour & BALLOON_DIALOG)))\r
411                                         {\r
412                                                 //mouse moved, so move the tooltip too\r
413                                                 CRect rect;\r
414                                                 GetWindowRect(rect);\r
415                                                 CalculateInfoBoxRect(&m_ptOriginal, &rect);\r
416                                                 SetWindowPos(NULL, rect.left, rect.top, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);\r
417                                         }\r
418                                         else\r
419                                                 return;\r
420                                 }\r
421                                 else if ((behaviour & BALLOON_MULTIPLE_SHOW)&&(!(behaviour & BALLOON_DIALOG)))\r
422                                 {\r
423                                         SetNewToolTip(CWnd::FromHandle(hWnd));\r
424                                 }\r
425                                 else\r
426                                         Pop();\r
427                         }\r
428                         else\r
429                         {\r
430                                 SetNewToolTip(CWnd::FromHandle(hWnd));\r
431                         }\r
432                 }\r
433                 break;\r
434         }\r
435 }\r
436 \r
437 void CBalloon::SetNewToolTip(CWnd * pWnd)\r
438 {\r
439         m_hDisplayedWnd = NULL;\r
440         Pop();\r
441 \r
442         if (!pWnd->IsWindowEnabled())\r
443                 return;\r
444 \r
445         if (!GetTool(pWnd, m_pToolInfo))\r
446                 return;\r
447 \r
448         m_hCurrentWnd = pWnd->GetSafeHwnd();\r
449 \r
450         SetTimer(BALLOON_SHOW, m_nTimeInitial, NULL);\r
451 }\r
452 \r
453 void CBalloon::OnTimer(UINT_PTR nIDEvent) \r
454 {\r
455         CPoint pt, point;\r
456         CString str;\r
457         HWND hWnd;\r
458 \r
459         switch (nIDEvent)\r
460         {\r
461         case BALLOON_SHOW:\r
462                 KillTimers(BALLOON_SHOW);\r
463                 //check if mouse pointer is still over the right window\r
464                 GetCursorPos(&pt);\r
465                 point = pt;\r
466                 if (m_pParentWnd)\r
467                         m_pParentWnd->ScreenToClient(&point);\r
468                 hWnd = GetChildWindowFromPoint(point);\r
469                 if (hWnd == m_hCurrentWnd)\r
470                 {\r
471                         DisplayToolTip(&pt);\r
472                         SetTimer(BALLOON_HIDE, m_nTimeAutoPop, NULL);\r
473                 }\r
474                 break;\r
475         case BALLOON_HIDE:\r
476                 KillTimers(BALLOON_HIDE);\r
477                 Pop();\r
478                 if (GetBehaviour() & BALLOON_DIALOG_DESTROY)\r
479                 {\r
480                         CWnd::OnTimer(nIDEvent);\r
481                         DestroyWindow();\r
482                         return;\r
483                 }\r
484                 break;\r
485         }\r
486         \r
487         CWnd::OnTimer(nIDEvent);\r
488 }\r
489 \r
490 HWND CBalloon::GetChildWindowFromPoint(CPoint & point) const\r
491 {\r
492         if (!m_pParentWnd)\r
493                 return NULL;\r
494     CPoint pt = point;\r
495     m_pParentWnd->ClientToScreen(&pt);\r
496     HWND hWnd = ::WindowFromPoint(pt);\r
497 \r
498     //::WindowFromPoint misses disabled windows and such - go for a more\r
499     //comprehensive search in this case.\r
500     if (hWnd == m_pParentWnd->GetSafeHwnd())\r
501         hWnd = m_pParentWnd->ChildWindowFromPoint(point, CWP_ALL)->GetSafeHwnd();\r
502 \r
503     //check that we aren't over the parent or out of client area\r
504     if (!hWnd || hWnd == m_pParentWnd->GetSafeHwnd())\r
505         return NULL;\r
506 \r
507     //if it's not part of the main parent window hierarchy, then we are\r
508     //not interested\r
509     if (!::IsChild(m_pParentWnd->GetSafeHwnd(), hWnd))\r
510         return NULL;\r
511 \r
512     return hWnd;\r
513 }\r
514 \r
515 BOOL CBalloon::IsCursorInToolTip() const\r
516 {\r
517     if (!IsVisible() || !IsWindow(m_hWnd))\r
518            return FALSE;\r
519 \r
520     CPoint pt;\r
521     GetCursorPos(&pt);\r
522 \r
523         CBalloon * pWnd = (CBalloon*)WindowFromPoint(pt);\r
524 \r
525         return (pWnd == this);\r
526 }\r
527 \r
528 void CBalloon::KillTimers(UINT nIDTimer /* = NULL */)\r
529 {\r
530         if (nIDTimer == NULL)\r
531         {\r
532                 KillTimer(BALLOON_SHOW);\r
533                 KillTimer(BALLOON_HIDE);\r
534         }\r
535         else if (nIDTimer == BALLOON_SHOW)\r
536                 KillTimer(BALLOON_SHOW);\r
537         else if (nIDTimer == BALLOON_HIDE)\r
538                 KillTimer(BALLOON_HIDE);\r
539 }\r
540 \r
541 void CBalloon::DisplayToolTip(CPoint * pt /* = NULL */)\r
542 {\r
543         if(!GetTool(CWnd::FromHandle(m_hCurrentWnd), m_pToolInfo) || m_pToolInfo.sBalloonTip.IsEmpty())\r
544                 return;\r
545         //if a mask is set then use the default values for the tooltip\r
546         if (!(m_pToolInfo.nMask & BALLOON_MASK_STYLES))\r
547                 m_pToolInfo.nStyles = m_nStyles;\r
548         if (!(m_pToolInfo.nMask & BALLOON_MASK_EFFECT))\r
549         {\r
550                 m_pToolInfo.nEffect = m_nEffect;\r
551         }\r
552         if (!(m_pToolInfo.nMask & BALLOON_MASK_COLORS))\r
553         {\r
554                 m_pToolInfo.crBegin = m_crColor[BALLOON_COLOR_BK_BEGIN];\r
555                 m_pToolInfo.crMid = m_crColor[BALLOON_COLOR_BK_MID];\r
556                 m_pToolInfo.crEnd = m_crColor[BALLOON_COLOR_BK_END];\r
557         }\r
558         if (!(m_pToolInfo.nMask & BALLOON_MASK_DIRECTION))\r
559                 m_pToolInfo.nDirection = m_nDirection;\r
560 \r
561         //send notification\r
562         SendNotify(CWnd::FromHandle(m_hCurrentWnd), pt, m_pToolInfo);\r
563 \r
564         //calculate the width and height of the box dynamically\r
565     CSize sz = GetTooltipSize(m_pToolInfo.sBalloonTip);\r
566         m_szBalloonText = sz;\r
567         \r
568         //get the size of the current icon\r
569         m_szBalloonIcon = GetSizeIcon(m_pToolInfo.hIcon);\r
570         if (m_szBalloonIcon.cx || m_szBalloonIcon.cy)\r
571         {\r
572                 sz.cx += m_szBalloonIcon.cx + m_nSizes[XBLSZ_MARGIN_CX];\r
573                 sz.cy = max(m_szBalloonIcon.cy, sz.cy);\r
574         }\r
575 \r
576         //get the size of the close button\r
577         m_szCloseButton = CSize(::GetSystemMetrics(SM_CXMENUSIZE), ::GetSystemMetrics(SM_CYMENUSIZE));\r
578         if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON)\r
579         {\r
580                 sz.cx += m_szCloseButton.cx + m_nSizes[XBLSZ_BUTTON_MARGIN_CX];\r
581         }\r
582 \r
583         //get size of the tooltip with margins\r
584         sz.cx += m_nSizes[XBLSZ_MARGIN_CX] * 2;\r
585         sz.cy += m_nSizes[XBLSZ_MARGIN_CY] * 2 + m_nSizes[XBLSZ_HEIGHT_ANCHOR];\r
586         if (m_pToolInfo.nStyles & BALLOON_SHADOW)\r
587         {\r
588                 sz.cx += m_nSizes[XBLSZ_SHADOW_CX];\r
589                 sz.cy += m_nSizes[XBLSZ_SHADOW_CY];\r
590         }\r
591         \r
592         CRect rect (0, 0, sz.cx, sz.cy);\r
593         \r
594         DisplayToolTip(pt, &rect);\r
595 }\r
596 \r
597 void CBalloon::DisplayToolTip(CPoint * pt, CRect * rect)\r
598 {\r
599         CalculateInfoBoxRect(pt, rect);\r
600 \r
601         SetWindowPos(\r
602                 NULL, rect->left, rect->top, rect->Width() + 2, rect->Height() + 2,\r
603                 SWP_SHOWWINDOW|SWP_NOCOPYBITS|SWP_NOACTIVATE|SWP_NOZORDER);\r
604 \r
605         CRgn rgn;\r
606         rgn.CreateRectRgn(0, 0, 1, 1);\r
607         if (m_pToolInfo.nStyles & BALLOON_SHADOW)\r
608         {\r
609                 rect->right -= m_nSizes[XBLSZ_SHADOW_CX];\r
610                 rect->bottom -= m_nSizes[XBLSZ_SHADOW_CY];\r
611         }\r
612 \r
613         m_rgnBalloon.DeleteObject();\r
614         GetWindowRegion(&m_rgnBalloon, CSize (rect->Width(), rect->Height()), *pt);\r
615         rgn.CopyRgn(&m_rgnBalloon);\r
616         if (m_pToolInfo.nStyles & BALLOON_SHADOW)\r
617         {\r
618                 m_rgnShadow.DeleteObject();\r
619                 m_rgnShadow.CreateRectRgn(0, 0, 1, 1);\r
620                 m_rgnShadow.CopyRgn(&m_rgnBalloon);\r
621                 m_rgnShadow.OffsetRgn(m_nSizes[XBLSZ_SHADOW_CX], m_nSizes[XBLSZ_SHADOW_CY]);\r
622                 rgn.CombineRgn(&rgn, &m_rgnShadow, RGN_OR);\r
623         }\r
624         SetWindowRgn(rgn, FALSE);\r
625 \r
626         rgn.DeleteObject();\r
627 }\r
628 \r
629 void CBalloon::Pop()\r
630 {\r
631         KillTimers();   \r
632         ShowWindow(SW_HIDE);\r
633         m_bButtonPushed = FALSE;\r
634 }\r
635 \r
636 CSize CBalloon::GetTooltipSize(const CString& str)\r
637 {\r
638         CRect rect;\r
639         GetWindowRect(&rect);\r
640 \r
641         CDC * pDC = GetDC();\r
642 \r
643         CDC memDC;\r
644         CBitmap bitmap;\r
645         memDC.CreateCompatibleDC(pDC);\r
646         bitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());\r
647         CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);\r
648 \r
649         //get the minimum size of the rectangle of the tooltip\r
650         CSize sz = DrawHTML(&memDC, rect, str, m_LogFont, TRUE);\r
651 \r
652         memDC.SelectObject(pOldBitmap);\r
653         memDC.DeleteDC();\r
654         bitmap.DeleteObject();\r
655 \r
656         ReleaseDC(pDC);\r
657 \r
658         return sz;\r
659 }\r
660 \r
661 CSize CBalloon::GetSizeIcon(HICON hIcon) const\r
662 {\r
663         ICONINFO ii;\r
664         CSize sz (0, 0);\r
665 \r
666         if (hIcon != NULL)\r
667         {\r
668                 //get icon dimensions\r
669                 ::SecureZeroMemory(&ii, sizeof(ICONINFO));\r
670                 if (::GetIconInfo(hIcon, &ii))\r
671                 {\r
672                         sz.cx = (DWORD)(ii.xHotspot * 2);\r
673                         sz.cy = (DWORD)(ii.yHotspot * 2);\r
674                         //release icon mask bitmaps\r
675                         if(ii.hbmMask)\r
676                                 ::DeleteObject(ii.hbmMask);\r
677                         if(ii.hbmColor)\r
678                                 ::DeleteObject(ii.hbmColor);\r
679                 }\r
680         }\r
681         return sz;\r
682 }\r
683 \r
684 void CBalloon::CalculateInfoBoxRect(CPoint * pt, CRect * rect)\r
685 {\r
686         CRect monitorRect;\r
687         GetMonitorWorkArea(*pt, monitorRect);\r
688 \r
689         CPoint ptEnd;\r
690         m_nLastDirection = m_pToolInfo.nDirection;\r
691         BOOL horzAdjusted = TestHorizDirection(pt->x, rect->Width(), monitorRect, m_nLastDirection, rect);\r
692         if (!horzAdjusted)\r
693         {\r
694                 m_nLastDirection = GetNextHorizDirection(m_nLastDirection);\r
695                 horzAdjusted = TestHorizDirection(pt->x, rect->Width(), monitorRect, m_nLastDirection, rect);\r
696         }\r
697         BOOL vertAdjusted = TestVertDirection(pt->y, rect->Height(), monitorRect, m_nLastDirection, rect);\r
698         if (!vertAdjusted)\r
699         {\r
700                 m_nLastDirection = GetNextVertDirection(m_nLastDirection);\r
701                 vertAdjusted = TestVertDirection(pt->y, rect->Height(), monitorRect, m_nLastDirection, rect);\r
702         }\r
703         // in case the rectangle wasn't adjusted which can happen if the tooltip is\r
704         // larger than half the monitor size, we center the tooltip around the mouse pointer\r
705         if (!horzAdjusted)\r
706         {\r
707                 int cx = rect->Width() / 2;\r
708                 rect->right = pt->x + cx;\r
709                 rect->left = pt->x - cx;\r
710         }\r
711         if (!vertAdjusted)\r
712         {\r
713                 int cy = rect->Height() / 2;\r
714                 rect->bottom = pt->y + cy;\r
715                 rect->top = pt->y - cy;\r
716         }\r
717         if ((m_pToolInfo.nStyles & BALLOON_SHADOW) && \r
718                 ((m_nLastDirection == BALLOON_LEFT_TOP) || (m_nLastDirection == BALLOON_LEFT_BOTTOM)))\r
719                 rect->OffsetRect(m_nSizes[XBLSZ_SHADOW_CX], m_nSizes[XBLSZ_SHADOW_CY]);\r
720 }\r
721 \r
722 \r
723 int CBalloon::GetNextHorizDirection(int nDirection) const\r
724 {\r
725         switch (nDirection)\r
726         {\r
727         case BALLOON_LEFT_TOP:\r
728                 nDirection = BALLOON_RIGHT_TOP;\r
729                 break;\r
730         case BALLOON_RIGHT_TOP:\r
731                 nDirection = BALLOON_LEFT_TOP;\r
732                 break;\r
733         case BALLOON_LEFT_BOTTOM:\r
734                 nDirection = BALLOON_RIGHT_BOTTOM;\r
735                 break;\r
736         case BALLOON_RIGHT_BOTTOM:\r
737                 nDirection = BALLOON_LEFT_BOTTOM;\r
738                 break;\r
739         }\r
740         return nDirection;\r
741 }\r
742 \r
743 int CBalloon::GetNextVertDirection(int nDirection) const\r
744 {\r
745         switch (nDirection)\r
746         {\r
747         case BALLOON_LEFT_TOP:\r
748                 nDirection = BALLOON_LEFT_BOTTOM;\r
749                 break;\r
750         case BALLOON_LEFT_BOTTOM:\r
751                 nDirection = BALLOON_LEFT_TOP;\r
752                 break;\r
753         case BALLOON_RIGHT_TOP:\r
754                 nDirection = BALLOON_RIGHT_BOTTOM;\r
755                 break;\r
756         case BALLOON_RIGHT_BOTTOM:\r
757                 nDirection = BALLOON_RIGHT_TOP;\r
758                 break;\r
759         }\r
760         return nDirection;\r
761 }\r
762 \r
763 BOOL CBalloon::TestHorizDirection(int x, int cx, const CRect& monitorRect,\r
764                                                                   int nDirection, LPRECT rect)\r
765 {\r
766         int left = 0;\r
767         int right = 0;\r
768         int anchorMarginSize = (int)m_nSizes[XBLSZ_MARGIN_ANCHOR];\r
769 \r
770         switch (nDirection)\r
771         {\r
772         case BALLOON_LEFT_TOP:\r
773         case BALLOON_LEFT_BOTTOM:\r
774                 right = ((x + anchorMarginSize) > monitorRect.right) ? monitorRect.right : (x + anchorMarginSize);\r
775                 left = right - cx;\r
776                 break;\r
777         case BALLOON_RIGHT_TOP:\r
778         case BALLOON_RIGHT_BOTTOM:\r
779                 left = (x - anchorMarginSize)<monitorRect.left ? monitorRect.left : (x - anchorMarginSize);\r
780                 right = left + cx;\r
781                 break;\r
782         }\r
783 \r
784         BOOL bTestOk = ((left >= monitorRect.left) && (right <= monitorRect.right)) ? TRUE : FALSE;\r
785         if (bTestOk)\r
786         {\r
787                 rect->left = left;\r
788                 rect->right = right;\r
789         }\r
790 \r
791         return bTestOk;\r
792 }\r
793 \r
794 BOOL CBalloon::TestVertDirection(int y, int cy, const CRect& monitorRect,\r
795                                                                  int nDirection, LPRECT rect)\r
796 {\r
797         int top = 0;\r
798         int bottom = 0;\r
799 \r
800         switch (nDirection)\r
801         {\r
802         case BALLOON_LEFT_TOP:\r
803         case BALLOON_RIGHT_TOP:\r
804                 bottom = y;\r
805                 top = bottom - cy;\r
806                 break;\r
807         case BALLOON_LEFT_BOTTOM:\r
808         case BALLOON_RIGHT_BOTTOM:\r
809                 top = y;\r
810                 bottom = top + cy;\r
811                 break;\r
812         }\r
813 \r
814         BOOL bTestOk = ((top >= monitorRect.top) && (bottom <= monitorRect.bottom)) ? TRUE : FALSE;\r
815         if (bTestOk)\r
816         {\r
817                 rect->top = top;\r
818                 rect->bottom = bottom;\r
819         }\r
820 \r
821         return bTestOk;\r
822 }\r
823 \r
824 LPLOGFONT CBalloon::GetSystemToolTipFont() const\r
825 {\r
826     static LOGFONT LogFont;\r
827 \r
828     NONCLIENTMETRICS ncm;\r
829     ncm.cbSize = sizeof(NONCLIENTMETRICS);\r
830     if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0))\r
831         return FALSE;\r
832 \r
833     memcpy(&LogFont, &(ncm.lfStatusFont), sizeof(LOGFONT));\r
834 \r
835     return &LogFont; \r
836 }\r
837 \r
838 \r
839 void CBalloon::Redraw(BOOL /*bRedraw*/ /* = TRUE */)\r
840 {\r
841 }\r
842 \r
843 void CBalloon::SetStyles(DWORD nStyles, CWnd * pWnd /* = NULL */)\r
844 {\r
845         ModifyStyles(nStyles, (DWORD)-1, pWnd);\r
846 }\r
847 \r
848 void CBalloon::ModifyStyles(DWORD nAddStyles, DWORD nRemoveStyles, CWnd * pWnd /* = NULL */)\r
849 {\r
850         if (!pWnd)\r
851         {\r
852                 m_nStyles &= ~nRemoveStyles;\r
853                 m_nStyles |= nAddStyles;\r
854         }\r
855         else\r
856         {\r
857                 BALLOON_INFO bi;\r
858                 if (GetTool(pWnd, bi))\r
859                 {\r
860                         if (!(bi.nMask & BALLOON_MASK_STYLES))\r
861                                 bi.nStyles = m_nStyles;\r
862                         bi.nStyles &= ~nRemoveStyles;\r
863                         bi.nStyles |= nAddStyles;\r
864                         bi.nMask |= BALLOON_MASK_STYLES;\r
865                         AddTool(pWnd, bi);\r
866                 }\r
867         }\r
868\r
869 \r
870 DWORD CBalloon::GetStyles(CWnd * pWnd /* = NULL */) const\r
871 {\r
872         if (pWnd)\r
873         {\r
874                 BALLOON_INFO bi;\r
875                 if (GetTool(pWnd, bi))\r
876                 {\r
877                         if (bi.nMask & BALLOON_MASK_STYLES)\r
878                                 return bi.nStyles;\r
879                 }\r
880         }\r
881         return m_nStyles;\r
882\r
883 \r
884 void CBalloon::SetDefaultStyles(CWnd * pWnd /* = NULL */)\r
885 {\r
886         SetStyles(BALLOON_RSA, pWnd);\r
887 }\r
888 \r
889 void CBalloon::SetColor(int nIndex, COLORREF crColor)\r
890 {\r
891         if (nIndex >= BALLOON_MAX_COLORS)\r
892                 return;\r
893 \r
894         m_crColor [nIndex] = crColor;\r
895 }\r
896 \r
897 COLORREF CBalloon::GetColor(int nIndex) const\r
898 {\r
899         if (nIndex >= BALLOON_MAX_COLORS)\r
900                 nIndex = BALLOON_COLOR_FG;\r
901 \r
902         return m_crColor [nIndex];\r
903 }\r
904 \r
905 void CBalloon::SetDefaultColors()\r
906 {\r
907         SetColor(BALLOON_COLOR_FG, ::GetSysColor(COLOR_INFOTEXT));\r
908         SetColor(BALLOON_COLOR_BK_BEGIN, ::GetSysColor(COLOR_INFOBK));\r
909         SetColor(BALLOON_COLOR_BK_MID, ::GetSysColor(COLOR_INFOBK));\r
910         SetColor(BALLOON_COLOR_BK_END, ::GetSysColor(COLOR_INFOBK));\r
911         SetColor(BALLOON_COLOR_SHADOW, ::GetSysColor(COLOR_3DSHADOW));\r
912         SetColor(BALLOON_COLOR_BORDER, ::GetSysColor(COLOR_WINDOWFRAME));\r
913 }\r
914 \r
915 void CBalloon::SetGradientColors(COLORREF crBegin, COLORREF crMid, COLORREF crEnd, CWnd * pWnd /* = NULL */)\r
916 {\r
917         if (!pWnd)\r
918         {\r
919                 SetColor(BALLOON_COLOR_BK_BEGIN, crBegin);\r
920                 SetColor(BALLOON_COLOR_BK_MID, crMid);\r
921                 SetColor(BALLOON_COLOR_BK_END, crEnd);\r
922         }\r
923         else\r
924         {\r
925                 BALLOON_INFO bi;\r
926                 if (GetTool(pWnd, bi))\r
927                 {\r
928                         bi.crBegin = crBegin;\r
929                         bi.crMid = crMid;\r
930                         bi.crEnd = crEnd;\r
931                         bi.nMask |= BALLOON_MASK_COLORS;\r
932                         AddTool(pWnd, bi);\r
933                 }\r
934         }\r
935 }\r
936 \r
937 void CBalloon::GetGradientColors(COLORREF & crBegin, COLORREF & crMid, COLORREF & crEnd, CWnd * pWnd /* = NULL */) const\r
938 {\r
939         if (pWnd)\r
940         {\r
941                 BALLOON_INFO bi;\r
942                 if (GetTool(pWnd, bi))\r
943                 {\r
944                         if (bi.nMask & BALLOON_MASK_COLORS)\r
945                         {\r
946                                 crBegin = bi.crBegin;\r
947                                 crMid = bi.crMid;\r
948                                 crEnd = bi.crEnd;\r
949                                 return;\r
950                         }\r
951                 }\r
952         }\r
953         crBegin = GetColor(BALLOON_COLOR_BK_BEGIN);\r
954         crMid = GetColor(BALLOON_COLOR_BK_MID);\r
955         crEnd = GetColor(BALLOON_COLOR_BK_END);\r
956\r
957 \r
958 void CBalloon::ShowBalloon(CWnd * pWnd, CPoint pt, UINT nIdText, BOOL showCloseButton, LPCTSTR szIcon)\r
959 {\r
960         CString str;\r
961         str.LoadString(nIdText);\r
962         HICON hIcon = (HICON)::LoadImage(NULL, szIcon, IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE);\r
963         ShowBalloon(pWnd, pt, str, showCloseButton, hIcon);\r
964 }\r
965 \r
966 void CBalloon::ShowBalloon(\r
967         CWnd * pWnd, CPoint pt, const CString& sText, BOOL showCloseButton, HICON hIcon, \r
968         UINT nDirection, UINT nEffect, COLORREF crStart, COLORREF crMid, COLORREF crEnd)\r
969 {\r
970         BALLOON_INFO Info;\r
971         Info.hIcon = hIcon;\r
972         Info.sBalloonTip = sText;\r
973         Info.nMask = 0;\r
974 \r
975         CBalloon * pSB = new CBalloon();\r
976         if (pWnd == NULL)\r
977                 pWnd = GetDesktopWindow();\r
978         pSB->Create(pWnd);\r
979         pSB->AddTool(pWnd, Info);\r
980         pSB->m_hCurrentWnd = pWnd->GetSafeHwnd();\r
981         pSB->SetDirection(nDirection);\r
982         pSB->SetEffectBk(nEffect);\r
983         if (crStart == NULL)\r
984                 crStart = ::GetSysColor(COLOR_INFOBK);\r
985         if (crMid == NULL)\r
986                 crMid = ::GetSysColor(COLOR_INFOBK);\r
987         if (crEnd == NULL)\r
988                 crEnd = ::GetSysColor(COLOR_INFOBK);\r
989         pSB->SetGradientColors(crStart, crMid, crEnd);\r
990         pSB->SetBehaviour(BALLOON_DIALOG | BALLOON_DIALOG_DESTROY);\r
991         if (showCloseButton)\r
992         {\r
993                 pSB->ModifyStyles(BALLOON_CLOSEBUTTON, 0);\r
994         }\r
995         pSB->DisplayToolTip(&pt);\r
996         if (!showCloseButton)\r
997                 pSB->SetTimer(BALLOON_HIDE, 5000, NULL); //auto close the dialog if no close button is shown\r
998 }\r
999 \r
1000 void CBalloon::AddTool(int nIdWnd, UINT nIdText, HICON hIcon/* = NULL*/)\r
1001 {\r
1002         AddTool(m_pParentWnd->GetDlgItem(nIdWnd), nIdText, hIcon);\r
1003 }\r
1004 void CBalloon::AddTool(int nIdWnd, UINT nIdText, UINT nIdIcon)\r
1005 {\r
1006         AddTool(m_pParentWnd->GetDlgItem(nIdWnd), nIdText, nIdIcon);\r
1007 }\r
1008 void CBalloon::AddTool(int nIdWnd, const CString& sBalloonTipText, HICON hIcon/* = NULL*/)\r
1009 {\r
1010         AddTool(m_pParentWnd->GetDlgItem(nIdWnd), sBalloonTipText, hIcon);\r
1011 }\r
1012 void CBalloon::AddTool(int nIdWnd, const CString& sBalloonTipText, UINT nIdIcon)\r
1013 {\r
1014         AddTool(m_pParentWnd->GetDlgItem(nIdWnd), sBalloonTipText, nIdIcon);\r
1015 }\r
1016 \r
1017 void CBalloon::AddTool(CWnd * pWnd, UINT nIdText, HICON hIcon /* = NULL */)\r
1018 {\r
1019         CString str;\r
1020     str.LoadString(nIdText);\r
1021         AddTool(pWnd, str, hIcon);\r
1022 }\r
1023 \r
1024 void CBalloon::AddTool(CWnd * pWnd, UINT nIdText, UINT nIdIcon)\r
1025 {\r
1026         CString str;\r
1027     str.LoadString(nIdText);\r
1028         AddTool(pWnd, str, nIdIcon);\r
1029\r
1030 \r
1031 void CBalloon::AddTool(CWnd * pWnd, const CString& sBalloonTipText, UINT nIdIcon)\r
1032 {\r
1033         HICON hIcon     = NULL;\r
1034         HINSTANCE hInstResource = NULL;\r
1035 \r
1036         if (nIdIcon >= 0)\r
1037         {\r
1038                 hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nIdIcon), RT_GROUP_ICON);\r
1039                 \r
1040                 hIcon = (HICON)::LoadImage(hInstResource, MAKEINTRESOURCE(nIdIcon), IMAGE_ICON, 0, 0, 0);\r
1041         }\r
1042 \r
1043         AddTool(pWnd, sBalloonTipText, hIcon);\r
1044\r
1045 \r
1046 void CBalloon::AddTool(CWnd * pWnd, const CString& sBalloonTipText, HICON hIcon /* = NULL */)\r
1047 {\r
1048         //store the tool information\r
1049         BALLOON_INFO Info;\r
1050         Info.hIcon = hIcon;\r
1051         Info.sBalloonTip = sBalloonTipText;\r
1052         Info.nMask = 0;\r
1053 \r
1054         AddTool(pWnd, Info);\r
1055 }\r
1056 \r
1057 void CBalloon::AddTool(CWnd * pWnd, BALLOON_INFO & bi)\r
1058 {\r
1059         if (pWnd)\r
1060                 m_ToolMap.SetAt(pWnd->m_hWnd, bi);\r
1061         else\r
1062                 m_ToolMap.SetAt(NULL, bi);\r
1063\r
1064 \r
1065 BOOL CBalloon::GetTool(CWnd * pWnd, CString & sBalloonTipText, HICON & hIcon) const\r
1066 {\r
1067         BALLOON_INFO bi;\r
1068         BOOL bFound = GetTool(pWnd, bi);\r
1069         if (bFound)\r
1070         {\r
1071                 sBalloonTipText = bi.sBalloonTip;\r
1072                 hIcon = bi.hIcon;\r
1073         }\r
1074 \r
1075         return bFound;\r
1076 }\r
1077 \r
1078 BOOL CBalloon::GetTool(CWnd * pWnd, BALLOON_INFO & bi) const\r
1079 {\r
1080         if (pWnd)\r
1081                 return m_ToolMap.Lookup(pWnd->m_hWnd, bi);\r
1082         return m_ToolMap.Lookup(NULL, bi);\r
1083 }\r
1084 \r
1085 void CBalloon::RemoveTool(CWnd * pWnd)\r
1086 {\r
1087         if (pWnd)\r
1088                 m_ToolMap.RemoveKey(pWnd->m_hWnd);\r
1089         m_ToolMap.RemoveKey(NULL);\r
1090 }\r
1091 \r
1092 void CBalloon::RemoveAllTools()\r
1093 {\r
1094         m_ToolMap.RemoveAll();\r
1095 }\r
1096 \r
1097 void CBalloon::SetMaskTool(CWnd * pWnd, UINT nMask /* = 0 */)\r
1098 {\r
1099         ModifyMaskTool(pWnd, nMask, (UINT)-1);\r
1100 }\r
1101 \r
1102 void CBalloon::ModifyMaskTool(CWnd * pWnd, UINT nAddMask, UINT nRemoveMask)\r
1103 {\r
1104         ASSERT(pWnd);\r
1105 \r
1106         BALLOON_INFO bi;\r
1107         if (GetTool(pWnd, bi))\r
1108         {\r
1109                 bi.nMask &= ~nRemoveMask;\r
1110                 bi.nMask |= nAddMask;\r
1111                 AddTool(pWnd, bi);\r
1112         }\r
1113 }\r
1114 \r
1115 UINT CBalloon::GetMaskTool(CWnd * pWnd) const\r
1116 {\r
1117         ASSERT(pWnd);\r
1118 \r
1119         UINT nMask = 0;\r
1120         BALLOON_INFO bi;\r
1121         if (GetTool(pWnd, bi))\r
1122                 nMask = bi.nMask;\r
1123         return nMask;\r
1124 }\r
1125 \r
1126 void CBalloon::SetEffectBk(UINT nEffect, CWnd * pWnd /* = NULL */)\r
1127 {\r
1128         if (!pWnd)\r
1129         {\r
1130                 m_nEffect = nEffect;\r
1131         }\r
1132         else\r
1133         {\r
1134                 BALLOON_INFO bi;\r
1135                 if (GetTool(pWnd, bi))\r
1136                 {\r
1137                         bi.nEffect = nEffect;\r
1138                         bi.nMask |= BALLOON_MASK_EFFECT;\r
1139                         AddTool(pWnd, bi);\r
1140                 }\r
1141         }\r
1142 }\r
1143 \r
1144 UINT CBalloon::GetEffectBk(CWnd * pWnd /* = NULL */) const\r
1145 {\r
1146         if (pWnd)\r
1147         {\r
1148                 BALLOON_INFO bi;\r
1149                 if (GetTool(pWnd, bi))\r
1150                 {\r
1151                         if (bi.nMask & BALLOON_MASK_EFFECT)\r
1152                         {\r
1153                                 return bi.nEffect;\r
1154                         }\r
1155                 }\r
1156         }\r
1157         return m_nEffect;\r
1158 }\r
1159 \r
1160 void CBalloon::SetNotify(BOOL bParentNotify /* = TRUE */)\r
1161 {\r
1162         HWND hWnd = NULL;\r
1163 \r
1164         if (bParentNotify)\r
1165                 hWnd = m_pParentWnd->GetSafeHwnd();\r
1166 \r
1167         SetNotify(hWnd);\r
1168 }\r
1169 \r
1170 void CBalloon::SetNotify(HWND hWnd)\r
1171 {\r
1172         m_hNotifyWnd = hWnd;\r
1173 }\r
1174 \r
1175 BOOL CBalloon::GetNotify() const\r
1176 {\r
1177         return (m_hNotifyWnd != NULL);\r
1178\r
1179 \r
1180 void CBalloon::SetDelayTime(DWORD dwDuration, UINT nTime)\r
1181 {\r
1182         switch (dwDuration)\r
1183         {\r
1184         case TTDT_AUTOPOP:\r
1185                 m_nTimeAutoPop = nTime;\r
1186                 break;\r
1187         case TTDT_INITIAL :\r
1188                 m_nTimeInitial = nTime;\r
1189                 break;\r
1190         }\r
1191 }\r
1192 \r
1193 UINT CBalloon::GetDelayTime(DWORD dwDuration) const\r
1194 {\r
1195         UINT nTime = 0;\r
1196         switch (dwDuration)\r
1197         {\r
1198         case TTDT_AUTOPOP:\r
1199                 nTime = m_nTimeAutoPop;\r
1200                 break;\r
1201         case TTDT_INITIAL:\r
1202                 nTime = m_nTimeInitial;\r
1203                 break;\r
1204         }\r
1205 \r
1206         return nTime;\r
1207\r
1208 \r
1209 void CBalloon::SetSize(int nSizeIndex, UINT nValue)\r
1210 {\r
1211         if (nSizeIndex >= XBLSZ_MAX_SIZES)\r
1212                 return;\r
1213 \r
1214         m_nSizes [nSizeIndex] = nValue;\r
1215 }\r
1216 \r
1217 UINT CBalloon::GetSize(int nSizeIndex) const\r
1218 {\r
1219         if (nSizeIndex >= XBLSZ_MAX_SIZES)\r
1220                 return 0;\r
1221 \r
1222         return m_nSizes [nSizeIndex];\r
1223 }\r
1224 \r
1225 void CBalloon::SetDefaultSizes()\r
1226 {\r
1227         SetSize(XBLSZ_ROUNDED_CX, 16);\r
1228         SetSize(XBLSZ_ROUNDED_CY, 16);\r
1229         SetSize(XBLSZ_MARGIN_CX, 12);\r
1230         SetSize(XBLSZ_MARGIN_CY, 12);\r
1231         SetSize(XBLSZ_SHADOW_CX, 4);\r
1232         SetSize(XBLSZ_SHADOW_CY, 4);\r
1233         SetSize(XBLSZ_WIDTH_ANCHOR, 12);\r
1234         SetSize(XBLSZ_HEIGHT_ANCHOR, 16);\r
1235         SetSize(XBLSZ_MARGIN_ANCHOR, 16);\r
1236         SetSize(XBLSZ_BORDER_CX, 1);\r
1237         SetSize(XBLSZ_BORDER_CY, 1);\r
1238         SetSize(XBLSZ_BUTTON_MARGIN_CX, 5);\r
1239         SetSize(XBLSZ_BUTTON_MARGIN_CY, 5);\r
1240 }\r
1241 \r
1242 void CBalloon::SetDirection(UINT nDirection /* = BALLOON_RIGHT_TOP */, CWnd * pWnd /* = NULL */)\r
1243 {\r
1244         if (nDirection >= BALLOON_MAX_DIRECTIONS)\r
1245                 return;\r
1246 \r
1247         if (!pWnd)\r
1248         {\r
1249                 m_nDirection = nDirection;\r
1250         }\r
1251         else\r
1252         {\r
1253                 BALLOON_INFO bi;\r
1254                 if (GetTool(pWnd, bi))\r
1255                 {\r
1256                         bi.nDirection = nDirection;\r
1257                         bi.nMask |= BALLOON_MASK_DIRECTION;\r
1258                         AddTool(pWnd, bi);\r
1259                 }\r
1260         }\r
1261 }\r
1262 \r
1263 UINT CBalloon::GetDirection(CWnd * pWnd /* = NULL */) const\r
1264 {\r
1265         if (pWnd)\r
1266         {\r
1267                 BALLOON_INFO bi;\r
1268                 if (GetTool(pWnd, bi))\r
1269                 {\r
1270                         if (bi.nMask & BALLOON_MASK_DIRECTION)\r
1271                                 return bi.nDirection;\r
1272                 }\r
1273         }\r
1274         return m_nDirection;\r
1275 }\r
1276 \r
1277 void CBalloon::SetBehaviour(UINT nBehaviour /* = 0 */, CWnd * pWnd /* = NULL */)\r
1278 {\r
1279         if (!pWnd)\r
1280         {\r
1281                 m_nBehaviour = nBehaviour;\r
1282         }\r
1283         else\r
1284         {\r
1285                 BALLOON_INFO bi;\r
1286                 if (GetTool(pWnd, bi))\r
1287                 {\r
1288                         bi.nBehaviour = nBehaviour;\r
1289                         bi.nMask |= BALLOON_MASK_BEHAVIOUR;\r
1290                         AddTool(pWnd, bi);\r
1291                 }\r
1292         }\r
1293 }\r
1294 \r
1295 UINT CBalloon::GetBehaviour(CWnd * pWnd /* = NULL */) const\r
1296 {\r
1297         if (pWnd)\r
1298         {\r
1299                 BALLOON_INFO bi;\r
1300                 if (GetTool(pWnd, bi))\r
1301                 {\r
1302                         if (bi.nMask & BALLOON_MASK_BEHAVIOUR)\r
1303                                 return bi.nBehaviour;\r
1304                 }\r
1305         }\r
1306         return m_nBehaviour;\r
1307 }\r
1308 \r
1309 BOOL CBalloon::SetFont(CFont & font)\r
1310 {\r
1311         LOGFONT lf;\r
1312         font.GetLogFont (&lf);\r
1313 \r
1314         return SetFont(&lf);\r
1315 }\r
1316 \r
1317 BOOL CBalloon::SetFont(LPLOGFONT lf)\r
1318 {\r
1319     memcpy(&m_LogFont, lf, sizeof(LOGFONT));\r
1320 \r
1321         return TRUE; \r
1322 }\r
1323 \r
1324 BOOL CBalloon::SetFont(LPCTSTR lpszFaceName, int nSizePoints /* = 8 */,\r
1325                                                                         BOOL bUnderline /* = FALSE */, BOOL bBold /* = FALSE */,\r
1326                                                                         BOOL bStrikeOut /* = FALSE */, BOOL bItalic /* = FALSE */)\r
1327 {\r
1328         CDC* pDC = GetDC();\r
1329         LOGFONT lf;\r
1330         memset (&lf, 0, sizeof(LOGFONT));\r
1331 \r
1332         _tcscpy_s (lf.lfFaceName, 32, lpszFaceName);\r
1333         lf.lfHeight = -MulDiv (nSizePoints, GetDeviceCaps (pDC->m_hDC, LOGPIXELSY), 72);\r
1334         lf.lfUnderline = (BYTE)bUnderline;\r
1335         lf.lfWeight = bBold ? FW_BOLD : FW_NORMAL;\r
1336         lf.lfStrikeOut = (BYTE)bStrikeOut;\r
1337         lf.lfItalic = (BYTE)bItalic;\r
1338 \r
1339         if (pDC)\r
1340                 ReleaseDC(pDC);\r
1341 \r
1342         return SetFont(&lf);\r
1343 }\r
1344 \r
1345 void CBalloon::SetDefaultFont()\r
1346 {\r
1347         LPLOGFONT lpSysFont = GetSystemToolTipFont();\r
1348 \r
1349         SetFont(lpSysFont);\r
1350\r
1351 \r
1352 void CBalloon::GetFont(CFont & font) const\r
1353 {\r
1354         font.CreateFontIndirect(&m_LogFont);\r
1355 }\r
1356 \r
1357 void CBalloon::GetFont(LPLOGFONT lf) const\r
1358 {\r
1359         memcpy(lf, &m_LogFont, sizeof(LOGFONT));\r
1360 }\r
1361 \r
1362 void CBalloon::OnMouseMove(UINT nFlags, CPoint point)\r
1363 {\r
1364         if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON)\r
1365         {\r
1366                 UINT nState = DFCS_CAPTIONCLOSE | DFCS_FLAT | DFCS_TRANSPARENT;\r
1367                 if (m_rtCloseButton.PtInRect(point))\r
1368                 {\r
1369                         nState |= DFCS_HOT;\r
1370                         if (m_bButtonPushed)\r
1371                                 nState |= DFCS_PUSHED;\r
1372                 }\r
1373                 CClientDC dc(this);\r
1374                 dc.DrawFrameControl(m_rtCloseButton, DFC_CAPTION, nState);\r
1375 \r
1376                 if (IsPointOverALink(point))\r
1377                         m_Cursor.SetCursor(IDC_HAND);\r
1378                 else\r
1379                         m_Cursor.Restore();     \r
1380         }\r
1381 \r
1382         CWnd::OnMouseMove(nFlags, point);\r
1383 }\r
1384 \r
1385 void CBalloon::OnLButtonDown(UINT nFlags, CPoint point)\r
1386 {\r
1387         if ((m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON) && m_rtCloseButton.PtInRect(point))\r
1388         {\r
1389                 m_bButtonPushed = TRUE;\r
1390                 OnMouseMove(0, point);\r
1391         }\r
1392 \r
1393         CWnd::OnLButtonDown(nFlags, point);\r
1394 }\r
1395 \r
1396 void CBalloon::OnLButtonUp(UINT nFlags, CPoint point)\r
1397 {\r
1398         if (IsPointOverALink(point))\r
1399         {\r
1400                 CString url = GetLinkForPoint(point);\r
1401                 ShellExecute(NULL, _T("open"), url, NULL,NULL, 0);\r
1402         }\r
1403         else if (\r
1404                 // Dialog has close button, but user has clicked somewhere else.\r
1405                 (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON) &&\r
1406                 (!m_rtCloseButton.PtInRect(point) || !m_bButtonPushed))\r
1407         {\r
1408                 m_bButtonPushed =  FALSE;\r
1409         }\r
1410         else\r
1411         {\r
1412                 Pop();\r
1413                 if (GetBehaviour() & BALLOON_DIALOG_DESTROY)\r
1414                 {\r
1415                         CWnd::OnLButtonUp(nFlags, point);\r
1416                         DestroyWindow();\r
1417                         return;\r
1418                 }\r
1419         }\r
1420         CWnd::OnLButtonUp(nFlags, point);\r
1421 }\r
1422 \r
1423 void CBalloon::PostNcDestroy()\r
1424 {\r
1425         CWnd::PostNcDestroy();\r
1426         TRACE("CBalloon: PostNcDestroy()\n");\r
1427 \r
1428         if (GetBehaviour() & BALLOON_DIALOG_DESTROY)\r
1429         {\r
1430                 TRACE("CBalloon: object deleted\n");\r
1431                 delete this;\r
1432         }\r
1433 }\r
1434 \r
1435 void CBalloon::GetMonitorWorkArea(const CPoint& sourcePoint, CRect& monitorRect) const\r
1436 {\r
1437         // identify the monitor that contains the sourcePoint\r
1438         // and return the work area (the portion of the screen \r
1439         // not obscured by the system task bar or by application \r
1440         // desktop tool bars) of that monitor\r
1441         OSVERSIONINFOEX VersionInformation;\r
1442         SecureZeroMemory(&VersionInformation, sizeof(OSVERSIONINFOEX));\r
1443         VersionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\r
1444         GetVersionEx((OSVERSIONINFO *)&VersionInformation);\r
1445 \r
1446         ::GetWindowRect(GetDesktopWindow()->m_hWnd, &monitorRect);\r
1447         \r
1448         if (VersionInformation.dwMajorVersion >= 5)\r
1449         {\r
1450                 MONITORINFO mi;\r
1451 \r
1452                 //\r
1453                 // get the work area\r
1454                 //\r
1455                 mi.cbSize = sizeof(mi);\r
1456                 HMODULE hUser32 = ::GetModuleHandle (_T("USER32.DLL"));\r
1457                 if (hUser32 != NULL)\r
1458                 {\r
1459                         typedef HMONITOR (WINAPI *FN_MonitorFromPoint) (POINT pt, DWORD dwFlags);\r
1460                         typedef BOOL (WINAPI *FN_GetMonitorInfo) (HMONITOR hMonitor, LPMONITORINFO lpmi);\r
1461                         FN_MonitorFromPoint pfnMonitorFromPoint = (FN_MonitorFromPoint)\r
1462                                 ::GetProcAddress (hUser32, "MonitorFromPoint");\r
1463                         FN_GetMonitorInfo pfnGetMonitorInfo = (FN_GetMonitorInfo)\r
1464                                 ::GetProcAddress (hUser32, "GetMonitorInfoW");\r
1465                         if (pfnMonitorFromPoint != NULL && pfnGetMonitorInfo != NULL)\r
1466                         {\r
1467                                 MONITORINFO mi;\r
1468                                 HMONITOR hMonitor = pfnMonitorFromPoint (sourcePoint, \r
1469                                         MONITOR_DEFAULTTONEAREST);\r
1470                                 mi.cbSize = sizeof (mi);\r
1471                                 pfnGetMonitorInfo (hMonitor, &mi);\r
1472                                 monitorRect = mi.rcWork;\r
1473                         }\r
1474                 }\r
1475         }\r
1476 \r
1477 }\r
1478 \r
1479 CPoint \r
1480 CBalloon::GetCtrlCentre(CWnd* pDlgWnd, UINT ctrlId)\r
1481 {\r
1482         CWnd* pCtrl = pDlgWnd->GetDlgItem(ctrlId);\r
1483         if(pCtrl == NULL)\r
1484         {\r
1485                 ASSERT(FALSE);\r
1486                 return CPoint(200,200);\r
1487         }\r
1488         CRect ctrlRect;\r
1489         pCtrl->GetWindowRect(ctrlRect);\r
1490         return ctrlRect.CenterPoint();\r
1491 }\r