1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseSVN
\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
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
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
20 #include "Balloon.h"
\r
22 tagBALLOON_INFO::tagBALLOON_INFO()
\r
36 CBalloon::CBalloon()
\r
39 m_pParentWnd = NULL;
\r
40 m_hCurrentWnd = NULL;
\r
41 m_hDisplayedWnd = NULL;
\r
43 m_rgnShadow.CreateRectRgn(0, 0, 1, 1);
\r
44 m_rgnBalloon.CreateRectRgn(0, 0, 1, 1);
\r
46 m_ptOriginal.x = -1;
\r
47 m_ptOriginal.y = -1;
\r
49 SetDelayTime(TTDT_INITIAL, 500);
\r
50 SetDelayTime(TTDT_AUTOPOP, 30000);
\r
57 SetEffectBk(BALLOON_EFFECT_SOLID);
\r
59 m_bButtonPushed = FALSE;
\r
61 // Register the window class if it has not already been registered.
\r
63 HINSTANCE hInst = AfxGetInstanceHandle();
\r
64 if(!(::GetClassInfo(hInst, BALLOON_CLASSNAME, &wndcls)))
\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
77 if (!AfxRegisterClass(&wndcls))
\r
78 AfxThrowResourceException();
\r
82 CBalloon::~CBalloon()
\r
86 m_rgnBalloon.DeleteObject();
\r
87 m_rgnShadow.DeleteObject();
\r
89 if (IsWindow(m_hWnd))
\r
94 BEGIN_MESSAGE_MAP(CBalloon, CWnd)
\r
95 //{{AFX_MSG_MAP(CBalloon)
\r
102 ON_WM_LBUTTONDOWN()
\r
107 /////////////////////////////////////////////////////////////////////////////
\r
108 // CBalloon message handlers
\r
110 BOOL CBalloon::Create(CWnd* pParentWnd)
\r
112 DWORD dwStyle = WS_POPUP;
\r
113 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
\r
115 m_pParentWnd = pParentWnd;
\r
117 if (!CreateEx(dwExStyle, BALLOON_CLASSNAME, NULL, dwStyle, 0, 0, 0, 0, m_pParentWnd->GetSafeHwnd(), NULL, NULL))
\r
126 void CBalloon::OnDestroy()
\r
128 TRACE("OnDestroy()\n");
\r
136 void CBalloon::OnKillFocus(CWnd* pNewWnd)
\r
138 CWnd::OnKillFocus(pNewWnd);
\r
143 BOOL CBalloon::PreTranslateMessage(MSG* pMsg)
\r
147 return CWnd::PreTranslateMessage(pMsg);
\r
150 LRESULT CBalloon::SendNotify(CWnd * pWnd, CPoint * pt, BALLOON_INFO & bi)
\r
152 //make sure this is a valid window
\r
153 if (!IsWindow(GetSafeHwnd()))
\r
156 //see if the user wants to be notified
\r
160 NM_BALLOON_DISPLAY lpnm;
\r
165 lpnm.hdr.hwndFrom = m_hWnd;
\r
166 lpnm.hdr.idFrom = GetDlgCtrlID();
\r
167 lpnm.hdr.code = UDM_TOOLTIP_DISPLAY;
\r
169 ::SendMessage(m_hNotifyWnd, WM_NOTIFY, lpnm.hdr.idFrom, (LPARAM)&lpnm);
\r
174 void CBalloon::OnPaint()
\r
176 //if (!m_pCurrentWnd)
\r
179 m_hDisplayedWnd = m_hCurrentWnd;
\r
181 CPaintDC dc(this); // device context for painting
\r
184 GetClientRect(&rect);
\r
185 rect.DeflateRect(0, 0, 1, 1);
\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
192 memDC.CreateCompatibleDC(&dc);
\r
193 bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
\r
194 CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);
\r
196 memDC.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &dc, 0, 0, SRCCOPY);
\r
199 OnDraw(&memDC, rect);
\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
204 //Cleanup resources.
\r
205 memDC.SelectObject(pOldBitmap);
\r
207 bitmap.DeleteObject();
\r
210 void CBalloon::OnDraw(CDC * pDC, CRect rect)
\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
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
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
227 pDC->SelectClipRgn(&m_rgnBalloon);
\r
229 OnDrawBackground(pDC, &rect);
\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
234 if ((m_nLastDirection == BALLOON_RIGHT_BOTTOM) || (m_nLastDirection == BALLOON_LEFT_BOTTOM))
\r
235 rect.top += m_nSizes[XBLSZ_HEIGHT_ANCHOR];
\r
237 rect.bottom -= m_nSizes[XBLSZ_HEIGHT_ANCHOR];
\r
239 if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON)
\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
248 //get the rectangle to draw the tooltip text
\r
249 rect.DeflateRect(m_nSizes[XBLSZ_MARGIN_CX], m_nSizes[XBLSZ_MARGIN_CY]);
\r
252 if (m_pToolInfo.hIcon != NULL)
\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
257 rect.left += m_szBalloonIcon.cx + m_nSizes[XBLSZ_MARGIN_CX];
\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
267 //prints the tool tip's text
\r
268 DrawHTML(pDC, rect, m_pToolInfo.sBalloonTip, m_LogFont, FALSE);
\r
271 brBackground.DeleteObject();
\r
272 brShadow.DeleteObject();
\r
273 brBorder.DeleteObject();
\r
276 void CBalloon::OnDrawBackground(CDC * pDC, CRect * pRect)
\r
278 #ifdef USE_GDI_GRADIENT
\r
279 #define DRAW CGradient::DrawGDI
\r
281 #define DRAW CGradient::Draw
\r
283 switch (m_pToolInfo.nEffect)
\r
285 case BALLOON_EFFECT_HGRADIENT:
\r
286 DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END]);
\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
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
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
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
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
305 pDC->FillSolidRect(pRect, m_crColor[BALLOON_COLOR_BK_BEGIN]);
\r
310 CRect CBalloon::GetWindowRegion(CRgn * rgn, CSize sz, CPoint pt) const
\r
313 rect.SetRect(0, 0, sz.cx, sz.cy);
\r
316 CPoint ptAnchor [3];
\r
318 ScreenToClient(&ptAnchor [0]);
\r
320 switch (m_nLastDirection)
\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
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
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
340 //gets the region for the anchor
\r
341 if (m_pToolInfo.nStyles & BALLOON_ANCHOR)
\r
343 switch (m_nLastDirection)
\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
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
356 rgnAnchor.CreatePolygonRgn(ptAnchor, 3, ALTERNATE);
\r
359 rgnAnchor.CreateRectRgn(0, 0, 0, 0);
\r
361 rgn->CreateRectRgn(0, 0, 1, 1);
\r
362 rgn->CombineRgn(&rgnRect, &rgnAnchor, RGN_OR);
\r
364 rgnAnchor.DeleteObject();
\r
365 rgnRect.DeleteObject();
\r
370 void CBalloon::RelayEvent(MSG* pMsg)
\r
379 switch(pMsg->message)
\r
382 if (m_ptOriginal == pMsg->pt)
\r
385 m_ptOriginal = pMsg->pt;
\r
387 //get the real window under the mouse pointer
\r
390 m_pParentWnd->ScreenToClient(&pt);
\r
391 hWnd = GetChildWindowFromPoint(pt);
\r
395 if (!(GetBehaviour() & BALLOON_DIALOG))
\r
398 m_hCurrentWnd = NULL;
\r
399 m_hDisplayedWnd = NULL;
\r
405 UINT behaviour = GetBehaviour(CWnd::FromHandle(hWnd));
\r
406 if (hWnd == m_hDisplayedWnd)
\r
408 if (IsWindowVisible())
\r
410 if ((behaviour & BALLOON_TRACK_MOUSE)&&(!(behaviour & BALLOON_DIALOG)))
\r
412 //mouse moved, so move the tooltip too
\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
421 else if ((behaviour & BALLOON_MULTIPLE_SHOW)&&(!(behaviour & BALLOON_DIALOG)))
\r
423 SetNewToolTip(CWnd::FromHandle(hWnd));
\r
430 SetNewToolTip(CWnd::FromHandle(hWnd));
\r
437 void CBalloon::SetNewToolTip(CWnd * pWnd)
\r
439 m_hDisplayedWnd = NULL;
\r
442 if (!pWnd->IsWindowEnabled())
\r
445 if (!GetTool(pWnd, m_pToolInfo))
\r
448 m_hCurrentWnd = pWnd->GetSafeHwnd();
\r
450 SetTimer(BALLOON_SHOW, m_nTimeInitial, NULL);
\r
453 void CBalloon::OnTimer(UINT_PTR nIDEvent)
\r
462 KillTimers(BALLOON_SHOW);
\r
463 //check if mouse pointer is still over the right window
\r
467 m_pParentWnd->ScreenToClient(&point);
\r
468 hWnd = GetChildWindowFromPoint(point);
\r
469 if (hWnd == m_hCurrentWnd)
\r
471 DisplayToolTip(&pt);
\r
472 SetTimer(BALLOON_HIDE, m_nTimeAutoPop, NULL);
\r
476 KillTimers(BALLOON_HIDE);
\r
478 if (GetBehaviour() & BALLOON_DIALOG_DESTROY)
\r
480 CWnd::OnTimer(nIDEvent);
\r
487 CWnd::OnTimer(nIDEvent);
\r
490 HWND CBalloon::GetChildWindowFromPoint(CPoint & point) const
\r
495 m_pParentWnd->ClientToScreen(&pt);
\r
496 HWND hWnd = ::WindowFromPoint(pt);
\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
503 //check that we aren't over the parent or out of client area
\r
504 if (!hWnd || hWnd == m_pParentWnd->GetSafeHwnd())
\r
507 //if it's not part of the main parent window hierarchy, then we are
\r
509 if (!::IsChild(m_pParentWnd->GetSafeHwnd(), hWnd))
\r
515 BOOL CBalloon::IsCursorInToolTip() const
\r
517 if (!IsVisible() || !IsWindow(m_hWnd))
\r
523 CBalloon * pWnd = (CBalloon*)WindowFromPoint(pt);
\r
525 return (pWnd == this);
\r
528 void CBalloon::KillTimers(UINT nIDTimer /* = NULL */)
\r
530 if (nIDTimer == NULL)
\r
532 KillTimer(BALLOON_SHOW);
\r
533 KillTimer(BALLOON_HIDE);
\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
541 void CBalloon::DisplayToolTip(CPoint * pt /* = NULL */)
\r
543 if(!GetTool(CWnd::FromHandle(m_hCurrentWnd), m_pToolInfo) || m_pToolInfo.sBalloonTip.IsEmpty())
\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
550 m_pToolInfo.nEffect = m_nEffect;
\r
552 if (!(m_pToolInfo.nMask & BALLOON_MASK_COLORS))
\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
558 if (!(m_pToolInfo.nMask & BALLOON_MASK_DIRECTION))
\r
559 m_pToolInfo.nDirection = m_nDirection;
\r
561 //send notification
\r
562 SendNotify(CWnd::FromHandle(m_hCurrentWnd), pt, m_pToolInfo);
\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
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
572 sz.cx += m_szBalloonIcon.cx + m_nSizes[XBLSZ_MARGIN_CX];
\r
573 sz.cy = max(m_szBalloonIcon.cy, sz.cy);
\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
580 sz.cx += m_szCloseButton.cx + m_nSizes[XBLSZ_BUTTON_MARGIN_CX];
\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
588 sz.cx += m_nSizes[XBLSZ_SHADOW_CX];
\r
589 sz.cy += m_nSizes[XBLSZ_SHADOW_CY];
\r
592 CRect rect (0, 0, sz.cx, sz.cy);
\r
594 DisplayToolTip(pt, &rect);
\r
597 void CBalloon::DisplayToolTip(CPoint * pt, CRect * rect)
\r
599 CalculateInfoBoxRect(pt, rect);
\r
602 NULL, rect->left, rect->top, rect->Width() + 2, rect->Height() + 2,
\r
603 SWP_SHOWWINDOW|SWP_NOCOPYBITS|SWP_NOACTIVATE|SWP_NOZORDER);
\r
606 rgn.CreateRectRgn(0, 0, 1, 1);
\r
607 if (m_pToolInfo.nStyles & BALLOON_SHADOW)
\r
609 rect->right -= m_nSizes[XBLSZ_SHADOW_CX];
\r
610 rect->bottom -= m_nSizes[XBLSZ_SHADOW_CY];
\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
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
624 SetWindowRgn(rgn, FALSE);
\r
626 rgn.DeleteObject();
\r
629 void CBalloon::Pop()
\r
632 ShowWindow(SW_HIDE);
\r
633 m_bButtonPushed = FALSE;
\r
636 CSize CBalloon::GetTooltipSize(const CString& str)
\r
639 GetWindowRect(&rect);
\r
641 CDC * pDC = GetDC();
\r
645 memDC.CreateCompatibleDC(pDC);
\r
646 bitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
\r
647 CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);
\r
649 //get the minimum size of the rectangle of the tooltip
\r
650 CSize sz = DrawHTML(&memDC, rect, str, m_LogFont, TRUE);
\r
652 memDC.SelectObject(pOldBitmap);
\r
654 bitmap.DeleteObject();
\r
661 CSize CBalloon::GetSizeIcon(HICON hIcon) const
\r
668 //get icon dimensions
\r
669 ::SecureZeroMemory(&ii, sizeof(ICONINFO));
\r
670 if (::GetIconInfo(hIcon, &ii))
\r
672 sz.cx = (DWORD)(ii.xHotspot * 2);
\r
673 sz.cy = (DWORD)(ii.yHotspot * 2);
\r
674 //release icon mask bitmaps
\r
676 ::DeleteObject(ii.hbmMask);
\r
678 ::DeleteObject(ii.hbmColor);
\r
684 void CBalloon::CalculateInfoBoxRect(CPoint * pt, CRect * rect)
\r
687 GetMonitorWorkArea(*pt, monitorRect);
\r
690 m_nLastDirection = m_pToolInfo.nDirection;
\r
691 BOOL horzAdjusted = TestHorizDirection(pt->x, rect->Width(), monitorRect, m_nLastDirection, rect);
\r
694 m_nLastDirection = GetNextHorizDirection(m_nLastDirection);
\r
695 horzAdjusted = TestHorizDirection(pt->x, rect->Width(), monitorRect, m_nLastDirection, rect);
\r
697 BOOL vertAdjusted = TestVertDirection(pt->y, rect->Height(), monitorRect, m_nLastDirection, rect);
\r
700 m_nLastDirection = GetNextVertDirection(m_nLastDirection);
\r
701 vertAdjusted = TestVertDirection(pt->y, rect->Height(), monitorRect, m_nLastDirection, rect);
\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
707 int cx = rect->Width() / 2;
\r
708 rect->right = pt->x + cx;
\r
709 rect->left = pt->x - cx;
\r
713 int cy = rect->Height() / 2;
\r
714 rect->bottom = pt->y + cy;
\r
715 rect->top = pt->y - cy;
\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
723 int CBalloon::GetNextHorizDirection(int nDirection) const
\r
725 switch (nDirection)
\r
727 case BALLOON_LEFT_TOP:
\r
728 nDirection = BALLOON_RIGHT_TOP;
\r
730 case BALLOON_RIGHT_TOP:
\r
731 nDirection = BALLOON_LEFT_TOP;
\r
733 case BALLOON_LEFT_BOTTOM:
\r
734 nDirection = BALLOON_RIGHT_BOTTOM;
\r
736 case BALLOON_RIGHT_BOTTOM:
\r
737 nDirection = BALLOON_LEFT_BOTTOM;
\r
743 int CBalloon::GetNextVertDirection(int nDirection) const
\r
745 switch (nDirection)
\r
747 case BALLOON_LEFT_TOP:
\r
748 nDirection = BALLOON_LEFT_BOTTOM;
\r
750 case BALLOON_LEFT_BOTTOM:
\r
751 nDirection = BALLOON_LEFT_TOP;
\r
753 case BALLOON_RIGHT_TOP:
\r
754 nDirection = BALLOON_RIGHT_BOTTOM;
\r
756 case BALLOON_RIGHT_BOTTOM:
\r
757 nDirection = BALLOON_RIGHT_TOP;
\r
763 BOOL CBalloon::TestHorizDirection(int x, int cx, const CRect& monitorRect,
\r
764 int nDirection, LPRECT rect)
\r
768 int anchorMarginSize = (int)m_nSizes[XBLSZ_MARGIN_ANCHOR];
\r
770 switch (nDirection)
\r
772 case BALLOON_LEFT_TOP:
\r
773 case BALLOON_LEFT_BOTTOM:
\r
774 right = ((x + anchorMarginSize) > monitorRect.right) ? monitorRect.right : (x + anchorMarginSize);
\r
777 case BALLOON_RIGHT_TOP:
\r
778 case BALLOON_RIGHT_BOTTOM:
\r
779 left = (x - anchorMarginSize)<monitorRect.left ? monitorRect.left : (x - anchorMarginSize);
\r
784 BOOL bTestOk = ((left >= monitorRect.left) && (right <= monitorRect.right)) ? TRUE : FALSE;
\r
788 rect->right = right;
\r
794 BOOL CBalloon::TestVertDirection(int y, int cy, const CRect& monitorRect,
\r
795 int nDirection, LPRECT rect)
\r
800 switch (nDirection)
\r
802 case BALLOON_LEFT_TOP:
\r
803 case BALLOON_RIGHT_TOP:
\r
807 case BALLOON_LEFT_BOTTOM:
\r
808 case BALLOON_RIGHT_BOTTOM:
\r
814 BOOL bTestOk = ((top >= monitorRect.top) && (bottom <= monitorRect.bottom)) ? TRUE : FALSE;
\r
818 rect->bottom = bottom;
\r
824 LPLOGFONT CBalloon::GetSystemToolTipFont() const
\r
826 static LOGFONT LogFont;
\r
828 NONCLIENTMETRICS ncm;
\r
829 ncm.cbSize = sizeof(NONCLIENTMETRICS);
\r
830 if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0))
\r
833 memcpy(&LogFont, &(ncm.lfStatusFont), sizeof(LOGFONT));
\r
839 void CBalloon::Redraw(BOOL /*bRedraw*/ /* = TRUE */)
\r
843 void CBalloon::SetStyles(DWORD nStyles, CWnd * pWnd /* = NULL */)
\r
845 ModifyStyles(nStyles, (DWORD)-1, pWnd);
\r
848 void CBalloon::ModifyStyles(DWORD nAddStyles, DWORD nRemoveStyles, CWnd * pWnd /* = NULL */)
\r
852 m_nStyles &= ~nRemoveStyles;
\r
853 m_nStyles |= nAddStyles;
\r
858 if (GetTool(pWnd, bi))
\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
870 DWORD CBalloon::GetStyles(CWnd * pWnd /* = NULL */) const
\r
875 if (GetTool(pWnd, bi))
\r
877 if (bi.nMask & BALLOON_MASK_STYLES)
\r
884 void CBalloon::SetDefaultStyles(CWnd * pWnd /* = NULL */)
\r
886 SetStyles(BALLOON_RSA, pWnd);
\r
889 void CBalloon::SetColor(int nIndex, COLORREF crColor)
\r
891 if (nIndex >= BALLOON_MAX_COLORS)
\r
894 m_crColor [nIndex] = crColor;
\r
897 COLORREF CBalloon::GetColor(int nIndex) const
\r
899 if (nIndex >= BALLOON_MAX_COLORS)
\r
900 nIndex = BALLOON_COLOR_FG;
\r
902 return m_crColor [nIndex];
\r
905 void CBalloon::SetDefaultColors()
\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
915 void CBalloon::SetGradientColors(COLORREF crBegin, COLORREF crMid, COLORREF crEnd, CWnd * pWnd /* = NULL */)
\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
926 if (GetTool(pWnd, bi))
\r
928 bi.crBegin = crBegin;
\r
931 bi.nMask |= BALLOON_MASK_COLORS;
\r
937 void CBalloon::GetGradientColors(COLORREF & crBegin, COLORREF & crMid, COLORREF & crEnd, CWnd * pWnd /* = NULL */) const
\r
942 if (GetTool(pWnd, bi))
\r
944 if (bi.nMask & BALLOON_MASK_COLORS)
\r
946 crBegin = bi.crBegin;
\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
958 void CBalloon::ShowBalloon(CWnd * pWnd, CPoint pt, UINT nIdText, BOOL showCloseButton, LPCTSTR szIcon)
\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
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
971 Info.hIcon = hIcon;
\r
972 Info.sBalloonTip = sText;
\r
975 CBalloon * pSB = new CBalloon();
\r
977 pWnd = GetDesktopWindow();
\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
986 crMid = ::GetSysColor(COLOR_INFOBK);
\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
993 pSB->ModifyStyles(BALLOON_CLOSEBUTTON, 0);
\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
1000 void CBalloon::AddTool(int nIdWnd, UINT nIdText, HICON hIcon/* = NULL*/)
\r
1002 AddTool(m_pParentWnd->GetDlgItem(nIdWnd), nIdText, hIcon);
\r
1004 void CBalloon::AddTool(int nIdWnd, UINT nIdText, UINT nIdIcon)
\r
1006 AddTool(m_pParentWnd->GetDlgItem(nIdWnd), nIdText, nIdIcon);
\r
1008 void CBalloon::AddTool(int nIdWnd, const CString& sBalloonTipText, HICON hIcon/* = NULL*/)
\r
1010 AddTool(m_pParentWnd->GetDlgItem(nIdWnd), sBalloonTipText, hIcon);
\r
1012 void CBalloon::AddTool(int nIdWnd, const CString& sBalloonTipText, UINT nIdIcon)
\r
1014 AddTool(m_pParentWnd->GetDlgItem(nIdWnd), sBalloonTipText, nIdIcon);
\r
1017 void CBalloon::AddTool(CWnd * pWnd, UINT nIdText, HICON hIcon /* = NULL */)
\r
1020 str.LoadString(nIdText);
\r
1021 AddTool(pWnd, str, hIcon);
\r
1024 void CBalloon::AddTool(CWnd * pWnd, UINT nIdText, UINT nIdIcon)
\r
1027 str.LoadString(nIdText);
\r
1028 AddTool(pWnd, str, nIdIcon);
\r
1031 void CBalloon::AddTool(CWnd * pWnd, const CString& sBalloonTipText, UINT nIdIcon)
\r
1033 HICON hIcon = NULL;
\r
1034 HINSTANCE hInstResource = NULL;
\r
1038 hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nIdIcon), RT_GROUP_ICON);
\r
1040 hIcon = (HICON)::LoadImage(hInstResource, MAKEINTRESOURCE(nIdIcon), IMAGE_ICON, 0, 0, 0);
\r
1043 AddTool(pWnd, sBalloonTipText, hIcon);
\r
1046 void CBalloon::AddTool(CWnd * pWnd, const CString& sBalloonTipText, HICON hIcon /* = NULL */)
\r
1048 //store the tool information
\r
1049 BALLOON_INFO Info;
\r
1050 Info.hIcon = hIcon;
\r
1051 Info.sBalloonTip = sBalloonTipText;
\r
1054 AddTool(pWnd, Info);
\r
1057 void CBalloon::AddTool(CWnd * pWnd, BALLOON_INFO & bi)
\r
1060 m_ToolMap.SetAt(pWnd->m_hWnd, bi);
\r
1062 m_ToolMap.SetAt(NULL, bi);
\r
1065 BOOL CBalloon::GetTool(CWnd * pWnd, CString & sBalloonTipText, HICON & hIcon) const
\r
1068 BOOL bFound = GetTool(pWnd, bi);
\r
1071 sBalloonTipText = bi.sBalloonTip;
\r
1078 BOOL CBalloon::GetTool(CWnd * pWnd, BALLOON_INFO & bi) const
\r
1081 return m_ToolMap.Lookup(pWnd->m_hWnd, bi);
\r
1082 return m_ToolMap.Lookup(NULL, bi);
\r
1085 void CBalloon::RemoveTool(CWnd * pWnd)
\r
1088 m_ToolMap.RemoveKey(pWnd->m_hWnd);
\r
1089 m_ToolMap.RemoveKey(NULL);
\r
1092 void CBalloon::RemoveAllTools()
\r
1094 m_ToolMap.RemoveAll();
\r
1097 void CBalloon::SetMaskTool(CWnd * pWnd, UINT nMask /* = 0 */)
\r
1099 ModifyMaskTool(pWnd, nMask, (UINT)-1);
\r
1102 void CBalloon::ModifyMaskTool(CWnd * pWnd, UINT nAddMask, UINT nRemoveMask)
\r
1107 if (GetTool(pWnd, bi))
\r
1109 bi.nMask &= ~nRemoveMask;
\r
1110 bi.nMask |= nAddMask;
\r
1111 AddTool(pWnd, bi);
\r
1115 UINT CBalloon::GetMaskTool(CWnd * pWnd) const
\r
1121 if (GetTool(pWnd, bi))
\r
1126 void CBalloon::SetEffectBk(UINT nEffect, CWnd * pWnd /* = NULL */)
\r
1130 m_nEffect = nEffect;
\r
1135 if (GetTool(pWnd, bi))
\r
1137 bi.nEffect = nEffect;
\r
1138 bi.nMask |= BALLOON_MASK_EFFECT;
\r
1139 AddTool(pWnd, bi);
\r
1144 UINT CBalloon::GetEffectBk(CWnd * pWnd /* = NULL */) const
\r
1149 if (GetTool(pWnd, bi))
\r
1151 if (bi.nMask & BALLOON_MASK_EFFECT)
\r
1153 return bi.nEffect;
\r
1160 void CBalloon::SetNotify(BOOL bParentNotify /* = TRUE */)
\r
1164 if (bParentNotify)
\r
1165 hWnd = m_pParentWnd->GetSafeHwnd();
\r
1170 void CBalloon::SetNotify(HWND hWnd)
\r
1172 m_hNotifyWnd = hWnd;
\r
1175 BOOL CBalloon::GetNotify() const
\r
1177 return (m_hNotifyWnd != NULL);
\r
1180 void CBalloon::SetDelayTime(DWORD dwDuration, UINT nTime)
\r
1182 switch (dwDuration)
\r
1184 case TTDT_AUTOPOP:
\r
1185 m_nTimeAutoPop = nTime;
\r
1187 case TTDT_INITIAL :
\r
1188 m_nTimeInitial = nTime;
\r
1193 UINT CBalloon::GetDelayTime(DWORD dwDuration) const
\r
1196 switch (dwDuration)
\r
1198 case TTDT_AUTOPOP:
\r
1199 nTime = m_nTimeAutoPop;
\r
1201 case TTDT_INITIAL:
\r
1202 nTime = m_nTimeInitial;
\r
1209 void CBalloon::SetSize(int nSizeIndex, UINT nValue)
\r
1211 if (nSizeIndex >= XBLSZ_MAX_SIZES)
\r
1214 m_nSizes [nSizeIndex] = nValue;
\r
1217 UINT CBalloon::GetSize(int nSizeIndex) const
\r
1219 if (nSizeIndex >= XBLSZ_MAX_SIZES)
\r
1222 return m_nSizes [nSizeIndex];
\r
1225 void CBalloon::SetDefaultSizes()
\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
1242 void CBalloon::SetDirection(UINT nDirection /* = BALLOON_RIGHT_TOP */, CWnd * pWnd /* = NULL */)
\r
1244 if (nDirection >= BALLOON_MAX_DIRECTIONS)
\r
1249 m_nDirection = nDirection;
\r
1254 if (GetTool(pWnd, bi))
\r
1256 bi.nDirection = nDirection;
\r
1257 bi.nMask |= BALLOON_MASK_DIRECTION;
\r
1258 AddTool(pWnd, bi);
\r
1263 UINT CBalloon::GetDirection(CWnd * pWnd /* = NULL */) const
\r
1268 if (GetTool(pWnd, bi))
\r
1270 if (bi.nMask & BALLOON_MASK_DIRECTION)
\r
1271 return bi.nDirection;
\r
1274 return m_nDirection;
\r
1277 void CBalloon::SetBehaviour(UINT nBehaviour /* = 0 */, CWnd * pWnd /* = NULL */)
\r
1281 m_nBehaviour = nBehaviour;
\r
1286 if (GetTool(pWnd, bi))
\r
1288 bi.nBehaviour = nBehaviour;
\r
1289 bi.nMask |= BALLOON_MASK_BEHAVIOUR;
\r
1290 AddTool(pWnd, bi);
\r
1295 UINT CBalloon::GetBehaviour(CWnd * pWnd /* = NULL */) const
\r
1300 if (GetTool(pWnd, bi))
\r
1302 if (bi.nMask & BALLOON_MASK_BEHAVIOUR)
\r
1303 return bi.nBehaviour;
\r
1306 return m_nBehaviour;
\r
1309 BOOL CBalloon::SetFont(CFont & font)
\r
1312 font.GetLogFont (&lf);
\r
1314 return SetFont(&lf);
\r
1317 BOOL CBalloon::SetFont(LPLOGFONT lf)
\r
1319 memcpy(&m_LogFont, lf, sizeof(LOGFONT));
\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
1328 CDC* pDC = GetDC();
\r
1330 memset (&lf, 0, sizeof(LOGFONT));
\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
1342 return SetFont(&lf);
\r
1345 void CBalloon::SetDefaultFont()
\r
1347 LPLOGFONT lpSysFont = GetSystemToolTipFont();
\r
1349 SetFont(lpSysFont);
\r
1352 void CBalloon::GetFont(CFont & font) const
\r
1354 font.CreateFontIndirect(&m_LogFont);
\r
1357 void CBalloon::GetFont(LPLOGFONT lf) const
\r
1359 memcpy(lf, &m_LogFont, sizeof(LOGFONT));
\r
1362 void CBalloon::OnMouseMove(UINT nFlags, CPoint point)
\r
1364 if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON)
\r
1366 UINT nState = DFCS_CAPTIONCLOSE | DFCS_FLAT | DFCS_TRANSPARENT;
\r
1367 if (m_rtCloseButton.PtInRect(point))
\r
1369 nState |= DFCS_HOT;
\r
1370 if (m_bButtonPushed)
\r
1371 nState |= DFCS_PUSHED;
\r
1373 CClientDC dc(this);
\r
1374 dc.DrawFrameControl(m_rtCloseButton, DFC_CAPTION, nState);
\r
1376 if (IsPointOverALink(point))
\r
1377 m_Cursor.SetCursor(IDC_HAND);
\r
1379 m_Cursor.Restore();
\r
1382 CWnd::OnMouseMove(nFlags, point);
\r
1385 void CBalloon::OnLButtonDown(UINT nFlags, CPoint point)
\r
1387 if ((m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON) && m_rtCloseButton.PtInRect(point))
\r
1389 m_bButtonPushed = TRUE;
\r
1390 OnMouseMove(0, point);
\r
1393 CWnd::OnLButtonDown(nFlags, point);
\r
1396 void CBalloon::OnLButtonUp(UINT nFlags, CPoint point)
\r
1398 if (IsPointOverALink(point))
\r
1400 CString url = GetLinkForPoint(point);
\r
1401 ShellExecute(NULL, _T("open"), url, NULL,NULL, 0);
\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
1408 m_bButtonPushed = FALSE;
\r
1413 if (GetBehaviour() & BALLOON_DIALOG_DESTROY)
\r
1415 CWnd::OnLButtonUp(nFlags, point);
\r
1420 CWnd::OnLButtonUp(nFlags, point);
\r
1423 void CBalloon::PostNcDestroy()
\r
1425 CWnd::PostNcDestroy();
\r
1426 TRACE("CBalloon: PostNcDestroy()\n");
\r
1428 if (GetBehaviour() & BALLOON_DIALOG_DESTROY)
\r
1430 TRACE("CBalloon: object deleted\n");
\r
1435 void CBalloon::GetMonitorWorkArea(const CPoint& sourcePoint, CRect& monitorRect) const
\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
1446 ::GetWindowRect(GetDesktopWindow()->m_hWnd, &monitorRect);
\r
1448 if (VersionInformation.dwMajorVersion >= 5)
\r
1453 // get the work area
\r
1455 mi.cbSize = sizeof(mi);
\r
1456 HMODULE hUser32 = ::GetModuleHandle (_T("USER32.DLL"));
\r
1457 if (hUser32 != NULL)
\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
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
1480 CBalloon::GetCtrlCentre(CWnd* pDlgWnd, UINT ctrlId)
\r
1482 CWnd* pCtrl = pDlgWnd->GetDlgItem(ctrlId);
\r
1486 return CPoint(200,200);
\r
1489 pCtrl->GetWindowRect(ctrlRect);
\r
1490 return ctrlRect.CenterPoint();
\r