added new log filter (for subject)
[TortoiseGit.git] / src / Utils / MiscUI / Balloon.cpp
blob5796e6302a2190092295d39d688e7fa466fc1c22
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include "Balloon.h"
22 tagBALLOON_INFO::tagBALLOON_INFO()
23 : hIcon(NULL),
24 sBalloonTip(),
25 nMask(0),
26 nStyles(0),
27 nDirection(0),
28 nEffect(0),
29 nBehaviour(0),
30 crBegin(0),
31 crMid(0),
32 crEnd(0)
36 CBalloon::CBalloon()
37 : m_nStyles (0)
39 m_pParentWnd = NULL;
40 m_hCurrentWnd = NULL;
41 m_hDisplayedWnd = NULL;
43 m_rgnShadow.CreateRectRgn(0, 0, 1, 1);
44 m_rgnBalloon.CreateRectRgn(0, 0, 1, 1);
46 m_ptOriginal.x = -1;
47 m_ptOriginal.y = -1;
49 SetDelayTime(TTDT_INITIAL, 500);
50 SetDelayTime(TTDT_AUTOPOP, 30000);
51 SetNotify(FALSE);
52 SetDirection();
53 SetBehaviour();
54 SetDefaultStyles();
55 SetDefaultColors();
56 SetDefaultSizes();
57 SetEffectBk(BALLOON_EFFECT_SOLID);
58 RemoveAllTools();
59 m_bButtonPushed = FALSE;
61 // Register the window class if it has not already been registered.
62 WNDCLASS wndcls;
63 HINSTANCE hInst = AfxGetInstanceHandle();
64 if(!(::GetClassInfo(hInst, BALLOON_CLASSNAME, &wndcls)))
66 // otherwise we need to register a new class
67 wndcls.style = CS_SAVEBITS;
68 wndcls.lpfnWndProc = ::DefWindowProc;
69 wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
70 wndcls.hInstance = hInst;
71 wndcls.hIcon = NULL;
72 wndcls.hCursor = LoadCursor(hInst, IDC_ARROW );
73 wndcls.hbrBackground = NULL;
74 wndcls.lpszMenuName = NULL;
75 wndcls.lpszClassName = BALLOON_CLASSNAME;
77 if (!AfxRegisterClass(&wndcls))
78 AfxThrowResourceException();
82 CBalloon::~CBalloon()
84 RemoveAllTools();
86 m_rgnBalloon.DeleteObject();
87 m_rgnShadow.DeleteObject();
89 if (IsWindow(m_hWnd))
90 DestroyWindow();
94 BEGIN_MESSAGE_MAP(CBalloon, CWnd)
95 //{{AFX_MSG_MAP(CBalloon)
96 ON_WM_PAINT()
97 ON_WM_TIMER()
98 ON_WM_DESTROY()
99 ON_WM_KILLFOCUS()
100 //}}AFX_MSG_MAP
101 ON_WM_MOUSEMOVE()
102 ON_WM_LBUTTONDOWN()
103 ON_WM_LBUTTONUP()
104 END_MESSAGE_MAP()
107 /////////////////////////////////////////////////////////////////////////////
108 // CBalloon message handlers
110 BOOL CBalloon::Create(CWnd* pParentWnd)
112 DWORD dwStyle = WS_POPUP;
113 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
115 m_pParentWnd = pParentWnd;
117 if (!CreateEx(dwExStyle, BALLOON_CLASSNAME, NULL, dwStyle, 0, 0, 0, 0, m_pParentWnd->GetSafeHwnd(), NULL, NULL))
119 return FALSE;
121 SetDefaultFont();
123 return TRUE;
126 void CBalloon::OnDestroy()
128 TRACE("OnDestroy()\n");
129 KillTimers();
131 CWnd::OnDestroy();
136 void CBalloon::OnKillFocus(CWnd* pNewWnd)
138 CWnd::OnKillFocus(pNewWnd);
139 Pop();
143 BOOL CBalloon::PreTranslateMessage(MSG* pMsg)
145 RelayEvent(pMsg);
147 return CWnd::PreTranslateMessage(pMsg);
150 LRESULT CBalloon::SendNotify(CWnd * pWnd, CPoint * pt, BALLOON_INFO & bi)
152 //make sure this is a valid window
153 if (!IsWindow(GetSafeHwnd()))
154 return 0L;
156 //see if the user wants to be notified
157 if (!GetNotify())
158 return 0L;
160 NM_BALLOON_DISPLAY lpnm;
162 lpnm.pWnd = pWnd;
163 lpnm.pt = pt;
164 lpnm.bi = &bi;
165 lpnm.hdr.hwndFrom = m_hWnd;
166 lpnm.hdr.idFrom = GetDlgCtrlID();
167 lpnm.hdr.code = UDM_TOOLTIP_DISPLAY;
169 ::SendMessage(m_hNotifyWnd, WM_NOTIFY, lpnm.hdr.idFrom, (LPARAM)&lpnm);
171 return 0L;
174 void CBalloon::OnPaint()
176 //if (!m_pCurrentWnd)
177 // return;
179 m_hDisplayedWnd = m_hCurrentWnd;
181 CPaintDC dc(this); // device context for painting
183 CRect rect;
184 GetClientRect(&rect);
185 rect.DeflateRect(0, 0, 1, 1);
187 //create a memory device-context. This is done to help reduce
188 //screen flicker, since we will paint the entire control to the
189 //off screen device context first.CDC memDC;
190 CDC memDC;
191 CBitmap bitmap;
192 memDC.CreateCompatibleDC(&dc);
193 bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
194 CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);
196 memDC.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &dc, 0, 0, SRCCOPY);
198 //draw the tooltip
199 OnDraw(&memDC, rect);
201 //Copy the memory device context back into the original DC.
202 dc.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &memDC, 0,0, SRCCOPY);
204 //Cleanup resources.
205 memDC.SelectObject(pOldBitmap);
206 memDC.DeleteDC();
207 bitmap.DeleteObject();
210 void CBalloon::OnDraw(CDC * pDC, CRect rect)
212 CBrush brBackground(m_crColor [BALLOON_COLOR_BK_BEGIN]);
213 CBrush brShadow(m_crColor [BALLOON_COLOR_SHADOW]);
214 CBrush brBorder(m_crColor [BALLOON_COLOR_BORDER]);
216 pDC->SetBkMode(TRANSPARENT);
217 pDC->SetTextColor(m_crColor [BALLOON_COLOR_FG]);
218 //set clip region of the tooltip and draw the shadow if needed
219 if (m_pToolInfo.nStyles & BALLOON_SHADOW)
221 //draw the shadow for the tooltip
222 int nRop2Mode = pDC->SetROP2(R2_MASKPEN);
223 pDC->FillRgn(&m_rgnShadow, &brShadow);
224 pDC->SetROP2(nRop2Mode);
225 rect.DeflateRect(0, 0, m_nSizes[XBLSZ_SHADOW_CX], m_nSizes[XBLSZ_SHADOW_CY]);
227 pDC->SelectClipRgn(&m_rgnBalloon);
229 OnDrawBackground(pDC, &rect);
231 //draw the main region's border of the tooltip
232 pDC->FrameRgn(&m_rgnBalloon, &brBorder, m_nSizes[XBLSZ_BORDER_CX], m_nSizes[XBLSZ_BORDER_CY]);
234 if ((m_nLastDirection == BALLOON_RIGHT_BOTTOM) || (m_nLastDirection == BALLOON_LEFT_BOTTOM))
235 rect.top += m_nSizes[XBLSZ_HEIGHT_ANCHOR];
236 else
237 rect.bottom -= m_nSizes[XBLSZ_HEIGHT_ANCHOR];
239 if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON)
241 m_rtCloseButton = CRect(
242 rect.right - m_szCloseButton.cx - m_nSizes[XBLSZ_BUTTON_MARGIN_CX] , rect.top + m_nSizes[XBLSZ_BUTTON_MARGIN_CY],
243 rect.right - m_nSizes[XBLSZ_BUTTON_MARGIN_CX], rect.top + m_szCloseButton.cy + m_nSizes[XBLSZ_BUTTON_MARGIN_CY]);
244 pDC->DrawFrameControl(m_rtCloseButton, DFC_CAPTION, DFCS_CAPTIONCLOSE|DFCS_FLAT|DFCS_TRANSPARENT);
245 rect.right -= (m_szCloseButton.cx + m_nSizes[XBLSZ_BUTTON_MARGIN_CX]);
248 //get the rectangle to draw the tooltip text
249 rect.DeflateRect(m_nSizes[XBLSZ_MARGIN_CX], m_nSizes[XBLSZ_MARGIN_CY]);
251 //draw the icon
252 if (m_pToolInfo.hIcon != NULL)
254 DrawIconEx(pDC->m_hDC, m_nSizes[XBLSZ_MARGIN_CX], rect.top + (rect.Height() - m_szBalloonIcon.cy) / 2,
255 m_pToolInfo.hIcon, m_szBalloonIcon.cx, m_szBalloonIcon.cy, 0, NULL, DI_NORMAL);
257 rect.left += m_szBalloonIcon.cx + m_nSizes[XBLSZ_MARGIN_CX];
261 //aligns tool tip's text
262 if (m_pToolInfo.nStyles & BALLOON_BOTTOM_ALIGN)
263 rect.top = rect.bottom - m_szBalloonText.cy;
264 else if (m_pToolInfo.nStyles & BALLOON_VCENTER_ALIGN)
265 rect.top += (rect.Height() - m_szBalloonText.cy) / 2;
267 //prints the tool tip's text
268 DrawHTML(pDC, rect, m_pToolInfo.sBalloonTip, m_LogFont, FALSE);
270 //free resources
271 brBackground.DeleteObject();
272 brShadow.DeleteObject();
273 brBorder.DeleteObject();
276 void CBalloon::OnDrawBackground(CDC * pDC, CRect * pRect)
278 #ifdef USE_GDI_GRADIENT
279 #define DRAW CGradient::DrawGDI
280 #else
281 #define DRAW CGradient::Draw
282 #endif
283 switch (m_pToolInfo.nEffect)
285 case BALLOON_EFFECT_HGRADIENT:
286 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END]);
287 break;
288 case BALLOON_EFFECT_VGRADIENT:
289 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END], FALSE);
290 break;
291 case BALLOON_EFFECT_HCGRADIENT:
292 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END], m_crColor[BALLOON_COLOR_BK_BEGIN]);
293 break;
294 case BALLOON_EFFECT_VCGRADIENT:
295 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END], m_crColor[BALLOON_COLOR_BK_BEGIN], FALSE);
296 break;
297 case BALLOON_EFFECT_3HGRADIENT:
298 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_MID], m_crColor[BALLOON_COLOR_BK_END]);
299 break;
300 case BALLOON_EFFECT_3VGRADIENT:
301 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_MID], m_crColor[BALLOON_COLOR_BK_END], FALSE);
302 break;
303 #undef DRAW
304 default:
305 pDC->FillSolidRect(pRect, m_crColor[BALLOON_COLOR_BK_BEGIN]);
306 break;
310 CRect CBalloon::GetWindowRegion(CRgn * rgn, CSize sz, CPoint pt) const
312 CRect rect;
313 rect.SetRect(0, 0, sz.cx, sz.cy);
314 CRgn rgnRect;
315 CRgn rgnAnchor;
316 CPoint ptAnchor [3];
317 ptAnchor [0] = pt;
318 ScreenToClient(&ptAnchor [0]);
320 switch (m_nLastDirection)
322 case BALLOON_LEFT_TOP:
323 case BALLOON_RIGHT_TOP:
324 rect.bottom -= m_nSizes[XBLSZ_HEIGHT_ANCHOR];
325 ptAnchor [1].y = ptAnchor [2].y = rect.bottom;
326 break;
327 case BALLOON_LEFT_BOTTOM:
328 case BALLOON_RIGHT_BOTTOM:
329 rect.top += m_nSizes[XBLSZ_HEIGHT_ANCHOR];
330 ptAnchor [1].y = ptAnchor [2].y = rect.top;
331 break;
334 //get the region for rectangle with the text
335 if (m_pToolInfo.nStyles & BALLOON_ROUNDED)
336 rgnRect.CreateRoundRectRgn(rect.left, rect.top, rect.right + 1, rect.bottom + 1,
337 m_nSizes[XBLSZ_ROUNDED_CX], m_nSizes[XBLSZ_ROUNDED_CY]);
338 else rgnRect.CreateRectRgn(rect.left, rect.top, rect.right + 1, rect.bottom + 1);
340 //gets the region for the anchor
341 if (m_pToolInfo.nStyles & BALLOON_ANCHOR)
343 switch (m_nLastDirection)
345 case BALLOON_LEFT_TOP:
346 case BALLOON_LEFT_BOTTOM:
347 ptAnchor [1].x = rect.right - m_nSizes[XBLSZ_MARGIN_ANCHOR];
348 ptAnchor [2].x = ptAnchor [1].x - m_nSizes[XBLSZ_WIDTH_ANCHOR];
349 break;
350 case BALLOON_RIGHT_TOP:
351 case BALLOON_RIGHT_BOTTOM:
352 ptAnchor [1].x = rect.left + m_nSizes[XBLSZ_MARGIN_ANCHOR];
353 ptAnchor [2].x = ptAnchor [1].x + m_nSizes[XBLSZ_WIDTH_ANCHOR];
354 break;
356 rgnAnchor.CreatePolygonRgn(ptAnchor, 3, ALTERNATE);
358 else
359 rgnAnchor.CreateRectRgn(0, 0, 0, 0);
361 rgn->CreateRectRgn(0, 0, 1, 1);
362 rgn->CombineRgn(&rgnRect, &rgnAnchor, RGN_OR);
364 rgnAnchor.DeleteObject();
365 rgnRect.DeleteObject();
367 return rect;
370 void CBalloon::RelayEvent(MSG* pMsg)
372 HWND hWnd = NULL;
373 CPoint pt;
374 CString str;
375 CRect rect;
377 BALLOON_INFO Info;
379 switch(pMsg->message)
381 case WM_MOUSEMOVE:
382 if (m_ptOriginal == pMsg->pt)
383 return;
385 m_ptOriginal = pMsg->pt;
387 //get the real window under the mouse pointer
388 pt = pMsg->pt;
389 if (m_pParentWnd)
390 m_pParentWnd->ScreenToClient(&pt);
391 hWnd = GetChildWindowFromPoint(pt);
393 if (!hWnd)
395 if (!(GetBehaviour() & BALLOON_DIALOG))
397 Pop();
398 m_hCurrentWnd = NULL;
399 m_hDisplayedWnd = NULL;
400 return;
403 else
405 UINT behaviour = GetBehaviour(CWnd::FromHandle(hWnd));
406 if (hWnd == m_hDisplayedWnd)
408 if (IsWindowVisible())
410 if ((behaviour & BALLOON_TRACK_MOUSE)&&(!(behaviour & BALLOON_DIALOG)))
412 //mouse moved, so move the tooltip too
413 CRect rect;
414 GetWindowRect(rect);
415 CalculateInfoBoxRect(&m_ptOriginal, &rect);
416 SetWindowPos(NULL, rect.left, rect.top, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
418 else
419 return;
421 else if ((behaviour & BALLOON_MULTIPLE_SHOW)&&(!(behaviour & BALLOON_DIALOG)))
423 SetNewToolTip(CWnd::FromHandle(hWnd));
425 else
426 Pop();
428 else
430 SetNewToolTip(CWnd::FromHandle(hWnd));
433 break;
437 void CBalloon::SetNewToolTip(CWnd * pWnd)
439 m_hDisplayedWnd = NULL;
440 Pop();
442 if (!pWnd->IsWindowEnabled())
443 return;
445 if (!GetTool(pWnd, m_pToolInfo))
446 return;
448 m_hCurrentWnd = pWnd->GetSafeHwnd();
450 SetTimer(BALLOON_SHOW, m_nTimeInitial, NULL);
453 void CBalloon::OnTimer(UINT_PTR nIDEvent)
455 CPoint pt, point;
456 CString str;
457 HWND hWnd;
459 switch (nIDEvent)
461 case BALLOON_SHOW:
462 KillTimers(BALLOON_SHOW);
463 //check if mouse pointer is still over the right window
464 GetCursorPos(&pt);
465 point = pt;
466 if (m_pParentWnd)
467 m_pParentWnd->ScreenToClient(&point);
468 hWnd = GetChildWindowFromPoint(point);
469 if (hWnd == m_hCurrentWnd)
471 DisplayToolTip(&pt);
472 SetTimer(BALLOON_HIDE, m_nTimeAutoPop, NULL);
474 break;
475 case BALLOON_HIDE:
476 KillTimers(BALLOON_HIDE);
477 Pop();
478 if (GetBehaviour() & BALLOON_DIALOG_DESTROY)
480 CWnd::OnTimer(nIDEvent);
481 DestroyWindow();
482 return;
484 break;
487 CWnd::OnTimer(nIDEvent);
490 HWND CBalloon::GetChildWindowFromPoint(CPoint & point) const
492 if (!m_pParentWnd)
493 return NULL;
494 CPoint pt = point;
495 m_pParentWnd->ClientToScreen(&pt);
496 HWND hWnd = ::WindowFromPoint(pt);
498 //::WindowFromPoint misses disabled windows and such - go for a more
499 //comprehensive search in this case.
500 if (hWnd == m_pParentWnd->GetSafeHwnd())
501 hWnd = m_pParentWnd->ChildWindowFromPoint(point, CWP_ALL)->GetSafeHwnd();
503 //check that we aren't over the parent or out of client area
504 if (!hWnd || hWnd == m_pParentWnd->GetSafeHwnd())
505 return NULL;
507 //if it's not part of the main parent window hierarchy, then we are
508 //not interested
509 if (!::IsChild(m_pParentWnd->GetSafeHwnd(), hWnd))
510 return NULL;
512 return hWnd;
515 BOOL CBalloon::IsCursorInToolTip() const
517 if (!IsVisible() || !IsWindow(m_hWnd))
518 return FALSE;
520 CPoint pt;
521 GetCursorPos(&pt);
523 CBalloon * pWnd = (CBalloon*)WindowFromPoint(pt);
525 return (pWnd == this);
528 void CBalloon::KillTimers(UINT nIDTimer /* = NULL */)
530 if (nIDTimer == NULL)
532 KillTimer(BALLOON_SHOW);
533 KillTimer(BALLOON_HIDE);
535 else if (nIDTimer == BALLOON_SHOW)
536 KillTimer(BALLOON_SHOW);
537 else if (nIDTimer == BALLOON_HIDE)
538 KillTimer(BALLOON_HIDE);
541 void CBalloon::DisplayToolTip(CPoint * pt /* = NULL */)
543 if(!GetTool(CWnd::FromHandle(m_hCurrentWnd), m_pToolInfo) || m_pToolInfo.sBalloonTip.IsEmpty())
544 return;
545 //if a mask is set then use the default values for the tooltip
546 if (!(m_pToolInfo.nMask & BALLOON_MASK_STYLES))
547 m_pToolInfo.nStyles = m_nStyles;
548 if (!(m_pToolInfo.nMask & BALLOON_MASK_EFFECT))
550 m_pToolInfo.nEffect = m_nEffect;
552 if (!(m_pToolInfo.nMask & BALLOON_MASK_COLORS))
554 m_pToolInfo.crBegin = m_crColor[BALLOON_COLOR_BK_BEGIN];
555 m_pToolInfo.crMid = m_crColor[BALLOON_COLOR_BK_MID];
556 m_pToolInfo.crEnd = m_crColor[BALLOON_COLOR_BK_END];
558 if (!(m_pToolInfo.nMask & BALLOON_MASK_DIRECTION))
559 m_pToolInfo.nDirection = m_nDirection;
561 //send notification
562 SendNotify(CWnd::FromHandle(m_hCurrentWnd), pt, m_pToolInfo);
564 //calculate the width and height of the box dynamically
565 CSize sz = GetTooltipSize(m_pToolInfo.sBalloonTip);
566 m_szBalloonText = sz;
568 //get the size of the current icon
569 m_szBalloonIcon = GetSizeIcon(m_pToolInfo.hIcon);
570 if (m_szBalloonIcon.cx || m_szBalloonIcon.cy)
572 sz.cx += m_szBalloonIcon.cx + m_nSizes[XBLSZ_MARGIN_CX];
573 sz.cy = max(m_szBalloonIcon.cy, sz.cy);
576 //get the size of the close button
577 m_szCloseButton = CSize(::GetSystemMetrics(SM_CXMENUSIZE), ::GetSystemMetrics(SM_CYMENUSIZE));
578 if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON)
580 sz.cx += m_szCloseButton.cx + m_nSizes[XBLSZ_BUTTON_MARGIN_CX];
583 //get size of the tooltip with margins
584 sz.cx += m_nSizes[XBLSZ_MARGIN_CX] * 2;
585 sz.cy += m_nSizes[XBLSZ_MARGIN_CY] * 2 + m_nSizes[XBLSZ_HEIGHT_ANCHOR];
586 if (m_pToolInfo.nStyles & BALLOON_SHADOW)
588 sz.cx += m_nSizes[XBLSZ_SHADOW_CX];
589 sz.cy += m_nSizes[XBLSZ_SHADOW_CY];
592 CRect rect (0, 0, sz.cx, sz.cy);
594 DisplayToolTip(pt, &rect);
597 void CBalloon::DisplayToolTip(CPoint * pt, CRect * rect)
599 CalculateInfoBoxRect(pt, rect);
601 SetWindowPos(
602 NULL, rect->left, rect->top, rect->Width() + 2, rect->Height() + 2,
603 SWP_SHOWWINDOW|SWP_NOCOPYBITS|SWP_NOACTIVATE|SWP_NOZORDER);
605 CRgn rgn;
606 rgn.CreateRectRgn(0, 0, 1, 1);
607 if (m_pToolInfo.nStyles & BALLOON_SHADOW)
609 rect->right -= m_nSizes[XBLSZ_SHADOW_CX];
610 rect->bottom -= m_nSizes[XBLSZ_SHADOW_CY];
613 m_rgnBalloon.DeleteObject();
614 GetWindowRegion(&m_rgnBalloon, CSize (rect->Width(), rect->Height()), *pt);
615 rgn.CopyRgn(&m_rgnBalloon);
616 if (m_pToolInfo.nStyles & BALLOON_SHADOW)
618 m_rgnShadow.DeleteObject();
619 m_rgnShadow.CreateRectRgn(0, 0, 1, 1);
620 m_rgnShadow.CopyRgn(&m_rgnBalloon);
621 m_rgnShadow.OffsetRgn(m_nSizes[XBLSZ_SHADOW_CX], m_nSizes[XBLSZ_SHADOW_CY]);
622 rgn.CombineRgn(&rgn, &m_rgnShadow, RGN_OR);
624 SetWindowRgn(rgn, FALSE);
626 rgn.DeleteObject();
629 void CBalloon::Pop()
631 KillTimers();
632 ShowWindow(SW_HIDE);
633 m_bButtonPushed = FALSE;
636 CSize CBalloon::GetTooltipSize(const CString& str)
638 CRect rect;
639 GetWindowRect(&rect);
641 CDC * pDC = GetDC();
643 CDC memDC;
644 CBitmap bitmap;
645 memDC.CreateCompatibleDC(pDC);
646 bitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
647 CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);
649 //get the minimum size of the rectangle of the tooltip
650 CSize sz = DrawHTML(&memDC, rect, str, m_LogFont, TRUE);
652 memDC.SelectObject(pOldBitmap);
653 memDC.DeleteDC();
654 bitmap.DeleteObject();
656 ReleaseDC(pDC);
658 return sz;
661 CSize CBalloon::GetSizeIcon(HICON hIcon) const
663 ICONINFO ii;
664 CSize sz (0, 0);
666 if (hIcon != NULL)
668 //get icon dimensions
669 ::SecureZeroMemory(&ii, sizeof(ICONINFO));
670 if (::GetIconInfo(hIcon, &ii))
672 sz.cx = (DWORD)(ii.xHotspot * 2);
673 sz.cy = (DWORD)(ii.yHotspot * 2);
674 //release icon mask bitmaps
675 if(ii.hbmMask)
676 ::DeleteObject(ii.hbmMask);
677 if(ii.hbmColor)
678 ::DeleteObject(ii.hbmColor);
681 return sz;
684 void CBalloon::CalculateInfoBoxRect(CPoint * pt, CRect * rect)
686 CRect monitorRect;
687 GetMonitorWorkArea(*pt, monitorRect);
689 CPoint ptEnd;
690 m_nLastDirection = m_pToolInfo.nDirection;
691 BOOL horzAdjusted = TestHorizDirection(pt->x, rect->Width(), monitorRect, m_nLastDirection, rect);
692 if (!horzAdjusted)
694 m_nLastDirection = GetNextHorizDirection(m_nLastDirection);
695 horzAdjusted = TestHorizDirection(pt->x, rect->Width(), monitorRect, m_nLastDirection, rect);
697 BOOL vertAdjusted = TestVertDirection(pt->y, rect->Height(), monitorRect, m_nLastDirection, rect);
698 if (!vertAdjusted)
700 m_nLastDirection = GetNextVertDirection(m_nLastDirection);
701 vertAdjusted = TestVertDirection(pt->y, rect->Height(), monitorRect, m_nLastDirection, rect);
703 // in case the rectangle wasn't adjusted which can happen if the tooltip is
704 // larger than half the monitor size, we center the tooltip around the mouse pointer
705 if (!horzAdjusted)
707 int cx = rect->Width() / 2;
708 rect->right = pt->x + cx;
709 rect->left = pt->x - cx;
711 if (!vertAdjusted)
713 int cy = rect->Height() / 2;
714 rect->bottom = pt->y + cy;
715 rect->top = pt->y - cy;
717 if ((m_pToolInfo.nStyles & BALLOON_SHADOW) &&
718 ((m_nLastDirection == BALLOON_LEFT_TOP) || (m_nLastDirection == BALLOON_LEFT_BOTTOM)))
719 rect->OffsetRect(m_nSizes[XBLSZ_SHADOW_CX], m_nSizes[XBLSZ_SHADOW_CY]);
723 int CBalloon::GetNextHorizDirection(int nDirection) const
725 switch (nDirection)
727 case BALLOON_LEFT_TOP:
728 nDirection = BALLOON_RIGHT_TOP;
729 break;
730 case BALLOON_RIGHT_TOP:
731 nDirection = BALLOON_LEFT_TOP;
732 break;
733 case BALLOON_LEFT_BOTTOM:
734 nDirection = BALLOON_RIGHT_BOTTOM;
735 break;
736 case BALLOON_RIGHT_BOTTOM:
737 nDirection = BALLOON_LEFT_BOTTOM;
738 break;
740 return nDirection;
743 int CBalloon::GetNextVertDirection(int nDirection) const
745 switch (nDirection)
747 case BALLOON_LEFT_TOP:
748 nDirection = BALLOON_LEFT_BOTTOM;
749 break;
750 case BALLOON_LEFT_BOTTOM:
751 nDirection = BALLOON_LEFT_TOP;
752 break;
753 case BALLOON_RIGHT_TOP:
754 nDirection = BALLOON_RIGHT_BOTTOM;
755 break;
756 case BALLOON_RIGHT_BOTTOM:
757 nDirection = BALLOON_RIGHT_TOP;
758 break;
760 return nDirection;
763 BOOL CBalloon::TestHorizDirection(int x, int cx, const CRect& monitorRect,
764 int nDirection, LPRECT rect)
766 int left = 0;
767 int right = 0;
768 int anchorMarginSize = (int)m_nSizes[XBLSZ_MARGIN_ANCHOR];
770 switch (nDirection)
772 case BALLOON_LEFT_TOP:
773 case BALLOON_LEFT_BOTTOM:
774 right = ((x + anchorMarginSize) > monitorRect.right) ? monitorRect.right : (x + anchorMarginSize);
775 left = right - cx;
776 break;
777 case BALLOON_RIGHT_TOP:
778 case BALLOON_RIGHT_BOTTOM:
779 left = (x - anchorMarginSize)<monitorRect.left ? monitorRect.left : (x - anchorMarginSize);
780 right = left + cx;
781 break;
784 BOOL bTestOk = ((left >= monitorRect.left) && (right <= monitorRect.right)) ? TRUE : FALSE;
785 if (bTestOk)
787 rect->left = left;
788 rect->right = right;
791 return bTestOk;
794 BOOL CBalloon::TestVertDirection(int y, int cy, const CRect& monitorRect,
795 int nDirection, LPRECT rect)
797 int top = 0;
798 int bottom = 0;
800 switch (nDirection)
802 case BALLOON_LEFT_TOP:
803 case BALLOON_RIGHT_TOP:
804 bottom = y;
805 top = bottom - cy;
806 break;
807 case BALLOON_LEFT_BOTTOM:
808 case BALLOON_RIGHT_BOTTOM:
809 top = y;
810 bottom = top + cy;
811 break;
814 BOOL bTestOk = ((top >= monitorRect.top) && (bottom <= monitorRect.bottom)) ? TRUE : FALSE;
815 if (bTestOk)
817 rect->top = top;
818 rect->bottom = bottom;
821 return bTestOk;
824 LPLOGFONT CBalloon::GetSystemToolTipFont() const
826 static LOGFONT LogFont;
828 NONCLIENTMETRICS ncm;
829 OSVERSIONINFO vers;
831 memset(&vers, 0, sizeof(OSVERSIONINFO));
832 ncm.cbSize = sizeof(NONCLIENTMETRICS);
833 vers.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
835 if(!GetVersionEx(&vers))
836 return NULL;
838 if(vers.dwMajorVersion < 6)
840 ncm.cbSize -= sizeof(ncm.iPaddedBorderWidth);
843 if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0))
844 return NULL;
846 memcpy(&LogFont, &(ncm.lfStatusFont), sizeof(LOGFONT));
848 return &LogFont;
852 void CBalloon::Redraw(BOOL /*bRedraw*/ /* = TRUE */)
856 void CBalloon::SetStyles(DWORD nStyles, CWnd * pWnd /* = NULL */)
858 ModifyStyles(nStyles, (DWORD)-1, pWnd);
861 void CBalloon::ModifyStyles(DWORD nAddStyles, DWORD nRemoveStyles, CWnd * pWnd /* = NULL */)
863 if (!pWnd)
865 m_nStyles &= ~nRemoveStyles;
866 m_nStyles |= nAddStyles;
868 else
870 BALLOON_INFO bi;
871 if (GetTool(pWnd, bi))
873 if (!(bi.nMask & BALLOON_MASK_STYLES))
874 bi.nStyles = m_nStyles;
875 bi.nStyles &= ~nRemoveStyles;
876 bi.nStyles |= nAddStyles;
877 bi.nMask |= BALLOON_MASK_STYLES;
878 AddTool(pWnd, bi);
883 DWORD CBalloon::GetStyles(CWnd * pWnd /* = NULL */) const
885 if (pWnd)
887 BALLOON_INFO bi;
888 if (GetTool(pWnd, bi))
890 if (bi.nMask & BALLOON_MASK_STYLES)
891 return bi.nStyles;
894 return m_nStyles;
897 void CBalloon::SetDefaultStyles(CWnd * pWnd /* = NULL */)
899 SetStyles(BALLOON_RSA, pWnd);
902 void CBalloon::SetColor(int nIndex, COLORREF crColor)
904 if (nIndex >= BALLOON_MAX_COLORS)
905 return;
907 m_crColor [nIndex] = crColor;
910 COLORREF CBalloon::GetColor(int nIndex) const
912 if (nIndex >= BALLOON_MAX_COLORS)
913 nIndex = BALLOON_COLOR_FG;
915 return m_crColor [nIndex];
918 void CBalloon::SetDefaultColors()
920 SetColor(BALLOON_COLOR_FG, ::GetSysColor(COLOR_INFOTEXT));
921 SetColor(BALLOON_COLOR_BK_BEGIN, ::GetSysColor(COLOR_INFOBK));
922 SetColor(BALLOON_COLOR_BK_MID, ::GetSysColor(COLOR_INFOBK));
923 SetColor(BALLOON_COLOR_BK_END, ::GetSysColor(COLOR_INFOBK));
924 SetColor(BALLOON_COLOR_SHADOW, ::GetSysColor(COLOR_3DSHADOW));
925 SetColor(BALLOON_COLOR_BORDER, ::GetSysColor(COLOR_WINDOWFRAME));
928 void CBalloon::SetGradientColors(COLORREF crBegin, COLORREF crMid, COLORREF crEnd, CWnd * pWnd /* = NULL */)
930 if (!pWnd)
932 SetColor(BALLOON_COLOR_BK_BEGIN, crBegin);
933 SetColor(BALLOON_COLOR_BK_MID, crMid);
934 SetColor(BALLOON_COLOR_BK_END, crEnd);
936 else
938 BALLOON_INFO bi;
939 if (GetTool(pWnd, bi))
941 bi.crBegin = crBegin;
942 bi.crMid = crMid;
943 bi.crEnd = crEnd;
944 bi.nMask |= BALLOON_MASK_COLORS;
945 AddTool(pWnd, bi);
950 void CBalloon::GetGradientColors(COLORREF & crBegin, COLORREF & crMid, COLORREF & crEnd, CWnd * pWnd /* = NULL */) const
952 if (pWnd)
954 BALLOON_INFO bi;
955 if (GetTool(pWnd, bi))
957 if (bi.nMask & BALLOON_MASK_COLORS)
959 crBegin = bi.crBegin;
960 crMid = bi.crMid;
961 crEnd = bi.crEnd;
962 return;
966 crBegin = GetColor(BALLOON_COLOR_BK_BEGIN);
967 crMid = GetColor(BALLOON_COLOR_BK_MID);
968 crEnd = GetColor(BALLOON_COLOR_BK_END);
971 void CBalloon::ShowBalloon(CWnd * pWnd, CPoint pt, UINT nIdText, BOOL showCloseButton, LPCTSTR szIcon)
973 CString str;
974 str.LoadString(nIdText);
975 HICON hIcon = (HICON)::LoadImage(NULL, szIcon, IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE);
976 ShowBalloon(pWnd, pt, str, showCloseButton, hIcon);
979 void CBalloon::ShowBalloon(
980 CWnd * pWnd, CPoint pt, const CString& sText, BOOL showCloseButton, HICON hIcon,
981 UINT nDirection, UINT nEffect, COLORREF crStart, COLORREF crMid, COLORREF crEnd)
983 BALLOON_INFO Info;
984 Info.hIcon = hIcon;
985 Info.sBalloonTip = sText;
986 Info.nMask = 0;
988 CBalloon * pSB = new CBalloon();
989 if (pWnd == NULL)
990 pWnd = GetDesktopWindow();
991 pSB->Create(pWnd);
992 pSB->AddTool(pWnd, Info);
993 pSB->m_hCurrentWnd = pWnd->GetSafeHwnd();
994 pSB->SetDirection(nDirection);
995 pSB->SetEffectBk(nEffect);
996 if (crStart == NULL)
997 crStart = ::GetSysColor(COLOR_INFOBK);
998 if (crMid == NULL)
999 crMid = ::GetSysColor(COLOR_INFOBK);
1000 if (crEnd == NULL)
1001 crEnd = ::GetSysColor(COLOR_INFOBK);
1002 pSB->SetGradientColors(crStart, crMid, crEnd);
1003 pSB->SetBehaviour(BALLOON_DIALOG | BALLOON_DIALOG_DESTROY);
1004 if (showCloseButton)
1006 pSB->ModifyStyles(BALLOON_CLOSEBUTTON, 0);
1008 pSB->DisplayToolTip(&pt);
1009 if (!showCloseButton)
1010 pSB->SetTimer(BALLOON_HIDE, 5000, NULL); //auto close the dialog if no close button is shown
1013 void CBalloon::AddTool(int nIdWnd, UINT nIdText, HICON hIcon/* = NULL*/)
1015 AddTool(m_pParentWnd->GetDlgItem(nIdWnd), nIdText, hIcon);
1017 void CBalloon::AddTool(int nIdWnd, UINT nIdText, UINT nIdIcon)
1019 AddTool(m_pParentWnd->GetDlgItem(nIdWnd), nIdText, nIdIcon);
1021 void CBalloon::AddTool(int nIdWnd, const CString& sBalloonTipText, HICON hIcon/* = NULL*/)
1023 AddTool(m_pParentWnd->GetDlgItem(nIdWnd), sBalloonTipText, hIcon);
1025 void CBalloon::AddTool(int nIdWnd, const CString& sBalloonTipText, UINT nIdIcon)
1027 AddTool(m_pParentWnd->GetDlgItem(nIdWnd), sBalloonTipText, nIdIcon);
1030 void CBalloon::AddTool(CWnd * pWnd, UINT nIdText, HICON hIcon /* = NULL */)
1032 CString str;
1033 str.LoadString(nIdText);
1034 AddTool(pWnd, str, hIcon);
1037 void CBalloon::AddTool(CWnd * pWnd, UINT nIdText, UINT nIdIcon)
1039 CString str;
1040 str.LoadString(nIdText);
1041 AddTool(pWnd, str, nIdIcon);
1044 void CBalloon::AddTool(CWnd * pWnd, const CString& sBalloonTipText, UINT nIdIcon)
1046 HICON hIcon = NULL;
1047 HINSTANCE hInstResource = NULL;
1049 if (nIdIcon >= 0)
1051 hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nIdIcon), RT_GROUP_ICON);
1053 hIcon = (HICON)::LoadImage(hInstResource, MAKEINTRESOURCE(nIdIcon), IMAGE_ICON, 0, 0, 0);
1056 AddTool(pWnd, sBalloonTipText, hIcon);
1059 void CBalloon::AddTool(CWnd * pWnd, const CString& sBalloonTipText, HICON hIcon /* = NULL */)
1061 //store the tool information
1062 BALLOON_INFO Info;
1063 Info.hIcon = hIcon;
1064 Info.sBalloonTip = sBalloonTipText;
1065 Info.nMask = 0;
1067 AddTool(pWnd, Info);
1070 void CBalloon::AddTool(CWnd * pWnd, BALLOON_INFO & bi)
1072 if (pWnd)
1073 m_ToolMap.SetAt(pWnd->m_hWnd, bi);
1074 else
1075 m_ToolMap.SetAt(NULL, bi);
1078 BOOL CBalloon::GetTool(CWnd * pWnd, CString & sBalloonTipText, HICON & hIcon) const
1080 BALLOON_INFO bi;
1081 BOOL bFound = GetTool(pWnd, bi);
1082 if (bFound)
1084 sBalloonTipText = bi.sBalloonTip;
1085 hIcon = bi.hIcon;
1088 return bFound;
1091 BOOL CBalloon::GetTool(CWnd * pWnd, BALLOON_INFO & bi) const
1093 if (pWnd)
1094 return m_ToolMap.Lookup(pWnd->m_hWnd, bi);
1095 return m_ToolMap.Lookup(NULL, bi);
1098 void CBalloon::RemoveTool(CWnd * pWnd)
1100 if (pWnd)
1101 m_ToolMap.RemoveKey(pWnd->m_hWnd);
1102 m_ToolMap.RemoveKey(NULL);
1105 void CBalloon::RemoveAllTools()
1107 m_ToolMap.RemoveAll();
1110 void CBalloon::SetMaskTool(CWnd * pWnd, UINT nMask /* = 0 */)
1112 ModifyMaskTool(pWnd, nMask, (UINT)-1);
1115 void CBalloon::ModifyMaskTool(CWnd * pWnd, UINT nAddMask, UINT nRemoveMask)
1117 ASSERT(pWnd);
1119 BALLOON_INFO bi;
1120 if (GetTool(pWnd, bi))
1122 bi.nMask &= ~nRemoveMask;
1123 bi.nMask |= nAddMask;
1124 AddTool(pWnd, bi);
1128 UINT CBalloon::GetMaskTool(CWnd * pWnd) const
1130 ASSERT(pWnd);
1132 UINT nMask = 0;
1133 BALLOON_INFO bi;
1134 if (GetTool(pWnd, bi))
1135 nMask = bi.nMask;
1136 return nMask;
1139 void CBalloon::SetEffectBk(UINT nEffect, CWnd * pWnd /* = NULL */)
1141 if (!pWnd)
1143 m_nEffect = nEffect;
1145 else
1147 BALLOON_INFO bi;
1148 if (GetTool(pWnd, bi))
1150 bi.nEffect = nEffect;
1151 bi.nMask |= BALLOON_MASK_EFFECT;
1152 AddTool(pWnd, bi);
1157 UINT CBalloon::GetEffectBk(CWnd * pWnd /* = NULL */) const
1159 if (pWnd)
1161 BALLOON_INFO bi;
1162 if (GetTool(pWnd, bi))
1164 if (bi.nMask & BALLOON_MASK_EFFECT)
1166 return bi.nEffect;
1170 return m_nEffect;
1173 void CBalloon::SetNotify(BOOL bParentNotify /* = TRUE */)
1175 HWND hWnd = NULL;
1177 if (bParentNotify)
1178 hWnd = m_pParentWnd->GetSafeHwnd();
1180 SetNotify(hWnd);
1183 void CBalloon::SetNotify(HWND hWnd)
1185 m_hNotifyWnd = hWnd;
1188 BOOL CBalloon::GetNotify() const
1190 return (m_hNotifyWnd != NULL);
1193 void CBalloon::SetDelayTime(DWORD dwDuration, UINT nTime)
1195 switch (dwDuration)
1197 case TTDT_AUTOPOP:
1198 m_nTimeAutoPop = nTime;
1199 break;
1200 case TTDT_INITIAL :
1201 m_nTimeInitial = nTime;
1202 break;
1206 UINT CBalloon::GetDelayTime(DWORD dwDuration) const
1208 UINT nTime = 0;
1209 switch (dwDuration)
1211 case TTDT_AUTOPOP:
1212 nTime = m_nTimeAutoPop;
1213 break;
1214 case TTDT_INITIAL:
1215 nTime = m_nTimeInitial;
1216 break;
1219 return nTime;
1222 void CBalloon::SetSize(int nSizeIndex, UINT nValue)
1224 if (nSizeIndex >= XBLSZ_MAX_SIZES)
1225 return;
1227 m_nSizes [nSizeIndex] = nValue;
1230 UINT CBalloon::GetSize(int nSizeIndex) const
1232 if (nSizeIndex >= XBLSZ_MAX_SIZES)
1233 return 0;
1235 return m_nSizes [nSizeIndex];
1238 void CBalloon::SetDefaultSizes()
1240 SetSize(XBLSZ_ROUNDED_CX, 16);
1241 SetSize(XBLSZ_ROUNDED_CY, 16);
1242 SetSize(XBLSZ_MARGIN_CX, 12);
1243 SetSize(XBLSZ_MARGIN_CY, 12);
1244 SetSize(XBLSZ_SHADOW_CX, 4);
1245 SetSize(XBLSZ_SHADOW_CY, 4);
1246 SetSize(XBLSZ_WIDTH_ANCHOR, 12);
1247 SetSize(XBLSZ_HEIGHT_ANCHOR, 16);
1248 SetSize(XBLSZ_MARGIN_ANCHOR, 16);
1249 SetSize(XBLSZ_BORDER_CX, 1);
1250 SetSize(XBLSZ_BORDER_CY, 1);
1251 SetSize(XBLSZ_BUTTON_MARGIN_CX, 5);
1252 SetSize(XBLSZ_BUTTON_MARGIN_CY, 5);
1255 void CBalloon::SetDirection(UINT nDirection /* = BALLOON_RIGHT_TOP */, CWnd * pWnd /* = NULL */)
1257 if (nDirection >= BALLOON_MAX_DIRECTIONS)
1258 return;
1260 if (!pWnd)
1262 m_nDirection = nDirection;
1264 else
1266 BALLOON_INFO bi;
1267 if (GetTool(pWnd, bi))
1269 bi.nDirection = nDirection;
1270 bi.nMask |= BALLOON_MASK_DIRECTION;
1271 AddTool(pWnd, bi);
1276 UINT CBalloon::GetDirection(CWnd * pWnd /* = NULL */) const
1278 if (pWnd)
1280 BALLOON_INFO bi;
1281 if (GetTool(pWnd, bi))
1283 if (bi.nMask & BALLOON_MASK_DIRECTION)
1284 return bi.nDirection;
1287 return m_nDirection;
1290 void CBalloon::SetBehaviour(UINT nBehaviour /* = 0 */, CWnd * pWnd /* = NULL */)
1292 if (!pWnd)
1294 m_nBehaviour = nBehaviour;
1296 else
1298 BALLOON_INFO bi;
1299 if (GetTool(pWnd, bi))
1301 bi.nBehaviour = nBehaviour;
1302 bi.nMask |= BALLOON_MASK_BEHAVIOUR;
1303 AddTool(pWnd, bi);
1308 UINT CBalloon::GetBehaviour(CWnd * pWnd /* = NULL */) const
1310 if (pWnd)
1312 BALLOON_INFO bi;
1313 if (GetTool(pWnd, bi))
1315 if (bi.nMask & BALLOON_MASK_BEHAVIOUR)
1316 return bi.nBehaviour;
1319 return m_nBehaviour;
1322 BOOL CBalloon::SetFont(CFont & font)
1324 LOGFONT lf;
1325 font.GetLogFont (&lf);
1327 return SetFont(&lf);
1330 BOOL CBalloon::SetFont(LPLOGFONT lf)
1332 memcpy(&m_LogFont, lf, sizeof(LOGFONT));
1334 return TRUE;
1337 BOOL CBalloon::SetFont(LPCTSTR lpszFaceName, int nSizePoints /* = 8 */,
1338 BOOL bUnderline /* = FALSE */, BOOL bBold /* = FALSE */,
1339 BOOL bStrikeOut /* = FALSE */, BOOL bItalic /* = FALSE */)
1341 CDC* pDC = GetDC();
1342 LOGFONT lf;
1343 memset (&lf, 0, sizeof(LOGFONT));
1345 _tcscpy_s (lf.lfFaceName, 32, lpszFaceName);
1346 lf.lfHeight = -MulDiv (nSizePoints, GetDeviceCaps (pDC->m_hDC, LOGPIXELSY), 72);
1347 lf.lfUnderline = (BYTE)bUnderline;
1348 lf.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
1349 lf.lfStrikeOut = (BYTE)bStrikeOut;
1350 lf.lfItalic = (BYTE)bItalic;
1352 if (pDC)
1353 ReleaseDC(pDC);
1355 return SetFont(&lf);
1358 void CBalloon::SetDefaultFont()
1360 LPLOGFONT lpSysFont = GetSystemToolTipFont();
1361 if(lpSysFont)
1362 SetFont(lpSysFont);
1365 void CBalloon::GetFont(CFont & font) const
1367 font.CreateFontIndirect(&m_LogFont);
1370 void CBalloon::GetFont(LPLOGFONT lf) const
1372 memcpy(lf, &m_LogFont, sizeof(LOGFONT));
1375 void CBalloon::OnMouseMove(UINT nFlags, CPoint point)
1377 if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON)
1379 UINT nState = DFCS_CAPTIONCLOSE | DFCS_FLAT | DFCS_TRANSPARENT;
1380 if (m_rtCloseButton.PtInRect(point))
1382 nState |= DFCS_HOT;
1383 if (m_bButtonPushed)
1384 nState |= DFCS_PUSHED;
1386 CClientDC dc(this);
1387 dc.DrawFrameControl(m_rtCloseButton, DFC_CAPTION, nState);
1389 if (IsPointOverALink(point))
1390 m_Cursor.SetCursor(IDC_HAND);
1391 else
1392 m_Cursor.Restore();
1395 CWnd::OnMouseMove(nFlags, point);
1398 void CBalloon::OnLButtonDown(UINT nFlags, CPoint point)
1400 if ((m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON) && m_rtCloseButton.PtInRect(point))
1402 m_bButtonPushed = TRUE;
1403 OnMouseMove(0, point);
1406 CWnd::OnLButtonDown(nFlags, point);
1409 void CBalloon::OnLButtonUp(UINT nFlags, CPoint point)
1411 if (IsPointOverALink(point))
1413 CString url = GetLinkForPoint(point);
1414 ShellExecute(NULL, _T("open"), url, NULL,NULL, 0);
1416 else if (
1417 // Dialog has close button, but user has clicked somewhere else.
1418 (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON) &&
1419 (!m_rtCloseButton.PtInRect(point) || !m_bButtonPushed))
1421 m_bButtonPushed = FALSE;
1423 else
1425 Pop();
1426 if (GetBehaviour() & BALLOON_DIALOG_DESTROY)
1428 CWnd::OnLButtonUp(nFlags, point);
1429 DestroyWindow();
1430 return;
1433 CWnd::OnLButtonUp(nFlags, point);
1436 void CBalloon::PostNcDestroy()
1438 CWnd::PostNcDestroy();
1439 TRACE("CBalloon: PostNcDestroy()\n");
1441 if (GetBehaviour() & BALLOON_DIALOG_DESTROY)
1443 TRACE("CBalloon: object deleted\n");
1444 delete this;
1448 void CBalloon::GetMonitorWorkArea(const CPoint& sourcePoint, CRect& monitorRect) const
1450 // identify the monitor that contains the sourcePoint
1451 // and return the work area (the portion of the screen
1452 // not obscured by the system task bar or by application
1453 // desktop tool bars) of that monitor
1454 OSVERSIONINFOEX VersionInformation;
1455 SecureZeroMemory(&VersionInformation, sizeof(OSVERSIONINFOEX));
1456 VersionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1457 GetVersionEx((OSVERSIONINFO *)&VersionInformation);
1459 ::GetWindowRect(GetDesktopWindow()->m_hWnd, &monitorRect);
1461 if (VersionInformation.dwMajorVersion >= 5)
1463 MONITORINFO mi;
1466 // get the work area
1468 mi.cbSize = sizeof(mi);
1469 HMODULE hUser32 = ::GetModuleHandle (_T("USER32.DLL"));
1470 if (hUser32 != NULL)
1472 typedef HMONITOR (WINAPI *FN_MonitorFromPoint) (POINT pt, DWORD dwFlags);
1473 typedef BOOL (WINAPI *FN_GetMonitorInfo) (HMONITOR hMonitor, LPMONITORINFO lpmi);
1474 FN_MonitorFromPoint pfnMonitorFromPoint = (FN_MonitorFromPoint)
1475 ::GetProcAddress (hUser32, "MonitorFromPoint");
1476 FN_GetMonitorInfo pfnGetMonitorInfo = (FN_GetMonitorInfo)
1477 ::GetProcAddress (hUser32, "GetMonitorInfoW");
1478 if (pfnMonitorFromPoint != NULL && pfnGetMonitorInfo != NULL)
1480 MONITORINFO mi;
1481 HMONITOR hMonitor = pfnMonitorFromPoint (sourcePoint,
1482 MONITOR_DEFAULTTONEAREST);
1483 mi.cbSize = sizeof (mi);
1484 pfnGetMonitorInfo (hMonitor, &mi);
1485 monitorRect = mi.rcWork;
1492 CPoint
1493 CBalloon::GetCtrlCentre(CWnd* pDlgWnd, UINT ctrlId)
1495 CWnd* pCtrl = pDlgWnd->GetDlgItem(ctrlId);
1496 if(pCtrl == NULL)
1498 ASSERT(FALSE);
1499 return CPoint(200,200);
1501 CRect ctrlRect;
1502 pCtrl->GetWindowRect(ctrlRect);
1503 return ctrlRect.CenterPoint();