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.
22 tagBALLOON_INFO::tagBALLOON_INFO()
41 m_hDisplayedWnd
= NULL
;
43 m_rgnShadow
.CreateRectRgn(0, 0, 1, 1);
44 m_rgnBalloon
.CreateRectRgn(0, 0, 1, 1);
49 SetDelayTime(TTDT_INITIAL
, 500);
50 SetDelayTime(TTDT_AUTOPOP
, 30000);
57 SetEffectBk(BALLOON_EFFECT_SOLID
);
59 m_bButtonPushed
= FALSE
;
61 // Register the window class if it has not already been registered.
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
;
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();
86 m_rgnBalloon
.DeleteObject();
87 m_rgnShadow
.DeleteObject();
94 BEGIN_MESSAGE_MAP(CBalloon
, CWnd
)
95 //{{AFX_MSG_MAP(CBalloon)
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
))
126 void CBalloon::OnDestroy()
128 TRACE("OnDestroy()\n");
136 void CBalloon::OnKillFocus(CWnd
* pNewWnd
)
138 CWnd::OnKillFocus(pNewWnd
);
143 BOOL
CBalloon::PreTranslateMessage(MSG
* 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()))
156 //see if the user wants to be notified
160 NM_BALLOON_DISPLAY lpnm
;
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
);
174 void CBalloon::OnPaint()
176 //if (!m_pCurrentWnd)
179 m_hDisplayedWnd
= m_hCurrentWnd
;
181 CPaintDC
dc(this); // device context for painting
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;
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
);
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
);
205 memDC
.SelectObject(pOldBitmap
);
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
];
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
]);
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
);
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
281 #define DRAW CGradient::Draw
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
]);
288 case BALLOON_EFFECT_VGRADIENT
:
289 DRAW(pDC
, pRect
, m_crColor
[BALLOON_COLOR_BK_BEGIN
], m_crColor
[BALLOON_COLOR_BK_END
], FALSE
);
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
]);
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
);
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
]);
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
);
305 pDC
->FillSolidRect(pRect
, m_crColor
[BALLOON_COLOR_BK_BEGIN
]);
310 CRect
CBalloon::GetWindowRegion(CRgn
* rgn
, CSize sz
, CPoint pt
) const
313 rect
.SetRect(0, 0, sz
.cx
, sz
.cy
);
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
;
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
;
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
];
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
];
356 rgnAnchor
.CreatePolygonRgn(ptAnchor
, 3, ALTERNATE
);
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();
370 void CBalloon::RelayEvent(MSG
* pMsg
)
379 switch(pMsg
->message
)
382 if (m_ptOriginal
== pMsg
->pt
)
385 m_ptOriginal
= pMsg
->pt
;
387 //get the real window under the mouse pointer
390 m_pParentWnd
->ScreenToClient(&pt
);
391 hWnd
= GetChildWindowFromPoint(pt
);
395 if (!(GetBehaviour() & BALLOON_DIALOG
))
398 m_hCurrentWnd
= NULL
;
399 m_hDisplayedWnd
= NULL
;
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
415 CalculateInfoBoxRect(&m_ptOriginal
, &rect
);
416 SetWindowPos(NULL
, rect
.left
, rect
.top
, 0, 0, SWP_SHOWWINDOW
|SWP_NOSIZE
|SWP_NOZORDER
|SWP_NOACTIVATE
);
421 else if ((behaviour
& BALLOON_MULTIPLE_SHOW
)&&(!(behaviour
& BALLOON_DIALOG
)))
423 SetNewToolTip(CWnd::FromHandle(hWnd
));
430 SetNewToolTip(CWnd::FromHandle(hWnd
));
437 void CBalloon::SetNewToolTip(CWnd
* pWnd
)
439 m_hDisplayedWnd
= NULL
;
442 if (!pWnd
->IsWindowEnabled())
445 if (!GetTool(pWnd
, m_pToolInfo
))
448 m_hCurrentWnd
= pWnd
->GetSafeHwnd();
450 SetTimer(BALLOON_SHOW
, m_nTimeInitial
, NULL
);
453 void CBalloon::OnTimer(UINT_PTR nIDEvent
)
462 KillTimers(BALLOON_SHOW
);
463 //check if mouse pointer is still over the right window
467 m_pParentWnd
->ScreenToClient(&point
);
468 hWnd
= GetChildWindowFromPoint(point
);
469 if (hWnd
== m_hCurrentWnd
)
472 SetTimer(BALLOON_HIDE
, m_nTimeAutoPop
, NULL
);
476 KillTimers(BALLOON_HIDE
);
478 if (GetBehaviour() & BALLOON_DIALOG_DESTROY
)
480 CWnd::OnTimer(nIDEvent
);
487 CWnd::OnTimer(nIDEvent
);
490 HWND
CBalloon::GetChildWindowFromPoint(CPoint
& point
) const
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())
507 //if it's not part of the main parent window hierarchy, then we are
509 if (!::IsChild(m_pParentWnd
->GetSafeHwnd(), hWnd
))
515 BOOL
CBalloon::IsCursorInToolTip() const
517 if (!IsVisible() || !IsWindow(m_hWnd
))
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())
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
;
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
);
602 NULL
, rect
->left
, rect
->top
, rect
->Width() + 2, rect
->Height() + 2,
603 SWP_SHOWWINDOW
|SWP_NOCOPYBITS
|SWP_NOACTIVATE
|SWP_NOZORDER
);
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
);
633 m_bButtonPushed
= FALSE
;
636 CSize
CBalloon::GetTooltipSize(const CString
& str
)
639 GetWindowRect(&rect
);
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
);
654 bitmap
.DeleteObject();
661 CSize
CBalloon::GetSizeIcon(HICON hIcon
) const
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
676 ::DeleteObject(ii
.hbmMask
);
678 ::DeleteObject(ii
.hbmColor
);
684 void CBalloon::CalculateInfoBoxRect(CPoint
* pt
, CRect
* rect
)
687 GetMonitorWorkArea(*pt
, monitorRect
);
690 m_nLastDirection
= m_pToolInfo
.nDirection
;
691 BOOL horzAdjusted
= TestHorizDirection(pt
->x
, rect
->Width(), monitorRect
, m_nLastDirection
, rect
);
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
);
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
707 int cx
= rect
->Width() / 2;
708 rect
->right
= pt
->x
+ cx
;
709 rect
->left
= pt
->x
- cx
;
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
727 case BALLOON_LEFT_TOP
:
728 nDirection
= BALLOON_RIGHT_TOP
;
730 case BALLOON_RIGHT_TOP
:
731 nDirection
= BALLOON_LEFT_TOP
;
733 case BALLOON_LEFT_BOTTOM
:
734 nDirection
= BALLOON_RIGHT_BOTTOM
;
736 case BALLOON_RIGHT_BOTTOM
:
737 nDirection
= BALLOON_LEFT_BOTTOM
;
743 int CBalloon::GetNextVertDirection(int nDirection
) const
747 case BALLOON_LEFT_TOP
:
748 nDirection
= BALLOON_LEFT_BOTTOM
;
750 case BALLOON_LEFT_BOTTOM
:
751 nDirection
= BALLOON_LEFT_TOP
;
753 case BALLOON_RIGHT_TOP
:
754 nDirection
= BALLOON_RIGHT_BOTTOM
;
756 case BALLOON_RIGHT_BOTTOM
:
757 nDirection
= BALLOON_RIGHT_TOP
;
763 BOOL
CBalloon::TestHorizDirection(int x
, int cx
, const CRect
& monitorRect
,
764 int nDirection
, LPRECT rect
)
768 int anchorMarginSize
= (int)m_nSizes
[XBLSZ_MARGIN_ANCHOR
];
772 case BALLOON_LEFT_TOP
:
773 case BALLOON_LEFT_BOTTOM
:
774 right
= ((x
+ anchorMarginSize
) > monitorRect
.right
) ? monitorRect
.right
: (x
+ anchorMarginSize
);
777 case BALLOON_RIGHT_TOP
:
778 case BALLOON_RIGHT_BOTTOM
:
779 left
= (x
- anchorMarginSize
)<monitorRect
.left
? monitorRect
.left
: (x
- anchorMarginSize
);
784 BOOL bTestOk
= ((left
>= monitorRect
.left
) && (right
<= monitorRect
.right
)) ? TRUE
: FALSE
;
794 BOOL
CBalloon::TestVertDirection(int y
, int cy
, const CRect
& monitorRect
,
795 int nDirection
, LPRECT rect
)
802 case BALLOON_LEFT_TOP
:
803 case BALLOON_RIGHT_TOP
:
807 case BALLOON_LEFT_BOTTOM
:
808 case BALLOON_RIGHT_BOTTOM
:
814 BOOL bTestOk
= ((top
>= monitorRect
.top
) && (bottom
<= monitorRect
.bottom
)) ? TRUE
: FALSE
;
818 rect
->bottom
= bottom
;
824 LPLOGFONT
CBalloon::GetSystemToolTipFont() const
826 static LOGFONT LogFont
;
828 NONCLIENTMETRICS ncm
;
831 memset(&vers
, 0, sizeof(OSVERSIONINFO
));
832 ncm
.cbSize
= sizeof(NONCLIENTMETRICS
);
833 vers
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
835 if(!GetVersionEx(&vers
))
838 if(vers
.dwMajorVersion
< 6)
840 ncm
.cbSize
-= sizeof(ncm
.iPaddedBorderWidth
);
843 if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICS
), &ncm
, 0))
846 memcpy(&LogFont
, &(ncm
.lfStatusFont
), sizeof(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 */)
865 m_nStyles
&= ~nRemoveStyles
;
866 m_nStyles
|= nAddStyles
;
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
;
883 DWORD
CBalloon::GetStyles(CWnd
* pWnd
/* = NULL */) const
888 if (GetTool(pWnd
, bi
))
890 if (bi
.nMask
& BALLOON_MASK_STYLES
)
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
)
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 */)
932 SetColor(BALLOON_COLOR_BK_BEGIN
, crBegin
);
933 SetColor(BALLOON_COLOR_BK_MID
, crMid
);
934 SetColor(BALLOON_COLOR_BK_END
, crEnd
);
939 if (GetTool(pWnd
, bi
))
941 bi
.crBegin
= crBegin
;
944 bi
.nMask
|= BALLOON_MASK_COLORS
;
950 void CBalloon::GetGradientColors(COLORREF
& crBegin
, COLORREF
& crMid
, COLORREF
& crEnd
, CWnd
* pWnd
/* = NULL */) const
955 if (GetTool(pWnd
, bi
))
957 if (bi
.nMask
& BALLOON_MASK_COLORS
)
959 crBegin
= bi
.crBegin
;
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
)
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
)
985 Info
.sBalloonTip
= sText
;
988 CBalloon
* pSB
= new CBalloon();
990 pWnd
= GetDesktopWindow();
992 pSB
->AddTool(pWnd
, Info
);
993 pSB
->m_hCurrentWnd
= pWnd
->GetSafeHwnd();
994 pSB
->SetDirection(nDirection
);
995 pSB
->SetEffectBk(nEffect
);
997 crStart
= ::GetSysColor(COLOR_INFOBK
);
999 crMid
= ::GetSysColor(COLOR_INFOBK
);
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 */)
1033 str
.LoadString(nIdText
);
1034 AddTool(pWnd
, str
, hIcon
);
1037 void CBalloon::AddTool(CWnd
* pWnd
, UINT nIdText
, UINT nIdIcon
)
1040 str
.LoadString(nIdText
);
1041 AddTool(pWnd
, str
, nIdIcon
);
1044 void CBalloon::AddTool(CWnd
* pWnd
, const CString
& sBalloonTipText
, UINT nIdIcon
)
1047 HINSTANCE hInstResource
= NULL
;
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
1064 Info
.sBalloonTip
= sBalloonTipText
;
1067 AddTool(pWnd
, Info
);
1070 void CBalloon::AddTool(CWnd
* pWnd
, BALLOON_INFO
& bi
)
1073 m_ToolMap
.SetAt(pWnd
->m_hWnd
, bi
);
1075 m_ToolMap
.SetAt(NULL
, bi
);
1078 BOOL
CBalloon::GetTool(CWnd
* pWnd
, CString
& sBalloonTipText
, HICON
& hIcon
) const
1081 BOOL bFound
= GetTool(pWnd
, bi
);
1084 sBalloonTipText
= bi
.sBalloonTip
;
1091 BOOL
CBalloon::GetTool(CWnd
* pWnd
, BALLOON_INFO
& bi
) const
1094 return m_ToolMap
.Lookup(pWnd
->m_hWnd
, bi
);
1095 return m_ToolMap
.Lookup(NULL
, bi
);
1098 void CBalloon::RemoveTool(CWnd
* 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
)
1120 if (GetTool(pWnd
, bi
))
1122 bi
.nMask
&= ~nRemoveMask
;
1123 bi
.nMask
|= nAddMask
;
1128 UINT
CBalloon::GetMaskTool(CWnd
* pWnd
) const
1134 if (GetTool(pWnd
, bi
))
1139 void CBalloon::SetEffectBk(UINT nEffect
, CWnd
* pWnd
/* = NULL */)
1143 m_nEffect
= nEffect
;
1148 if (GetTool(pWnd
, bi
))
1150 bi
.nEffect
= nEffect
;
1151 bi
.nMask
|= BALLOON_MASK_EFFECT
;
1157 UINT
CBalloon::GetEffectBk(CWnd
* pWnd
/* = NULL */) const
1162 if (GetTool(pWnd
, bi
))
1164 if (bi
.nMask
& BALLOON_MASK_EFFECT
)
1173 void CBalloon::SetNotify(BOOL bParentNotify
/* = TRUE */)
1178 hWnd
= m_pParentWnd
->GetSafeHwnd();
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
)
1198 m_nTimeAutoPop
= nTime
;
1201 m_nTimeInitial
= nTime
;
1206 UINT
CBalloon::GetDelayTime(DWORD dwDuration
) const
1212 nTime
= m_nTimeAutoPop
;
1215 nTime
= m_nTimeInitial
;
1222 void CBalloon::SetSize(int nSizeIndex
, UINT nValue
)
1224 if (nSizeIndex
>= XBLSZ_MAX_SIZES
)
1227 m_nSizes
[nSizeIndex
] = nValue
;
1230 UINT
CBalloon::GetSize(int nSizeIndex
) const
1232 if (nSizeIndex
>= XBLSZ_MAX_SIZES
)
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
)
1262 m_nDirection
= nDirection
;
1267 if (GetTool(pWnd
, bi
))
1269 bi
.nDirection
= nDirection
;
1270 bi
.nMask
|= BALLOON_MASK_DIRECTION
;
1276 UINT
CBalloon::GetDirection(CWnd
* pWnd
/* = NULL */) const
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 */)
1294 m_nBehaviour
= nBehaviour
;
1299 if (GetTool(pWnd
, bi
))
1301 bi
.nBehaviour
= nBehaviour
;
1302 bi
.nMask
|= BALLOON_MASK_BEHAVIOUR
;
1308 UINT
CBalloon::GetBehaviour(CWnd
* pWnd
/* = NULL */) const
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
)
1325 font
.GetLogFont (&lf
);
1327 return SetFont(&lf
);
1330 BOOL
CBalloon::SetFont(LPLOGFONT lf
)
1332 memcpy(&m_LogFont
, lf
, sizeof(LOGFONT
));
1337 BOOL
CBalloon::SetFont(LPCTSTR lpszFaceName
, int nSizePoints
/* = 8 */,
1338 BOOL bUnderline
/* = FALSE */, BOOL bBold
/* = FALSE */,
1339 BOOL bStrikeOut
/* = FALSE */, BOOL bItalic
/* = FALSE */)
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
;
1355 return SetFont(&lf
);
1358 void CBalloon::SetDefaultFont()
1360 LPLOGFONT lpSysFont
= GetSystemToolTipFont();
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
))
1383 if (m_bButtonPushed
)
1384 nState
|= DFCS_PUSHED
;
1387 dc
.DrawFrameControl(m_rtCloseButton
, DFC_CAPTION
, nState
);
1389 if (IsPointOverALink(point
))
1390 m_Cursor
.SetCursor(IDC_HAND
);
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);
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
;
1426 if (GetBehaviour() & BALLOON_DIALOG_DESTROY
)
1428 CWnd::OnLButtonUp(nFlags
, point
);
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");
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)
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
)
1481 HMONITOR hMonitor
= pfnMonitorFromPoint (sourcePoint
,
1482 MONITOR_DEFAULTTONEAREST
);
1483 mi
.cbSize
= sizeof (mi
);
1484 pfnGetMonitorInfo (hMonitor
, &mi
);
1485 monitorRect
= mi
.rcWork
;
1493 CBalloon::GetCtrlCentre(CWnd
* pDlgWnd
, UINT ctrlId
)
1495 CWnd
* pCtrl
= pDlgWnd
->GetDlgItem(ctrlId
);
1499 return CPoint(200,200);
1502 pCtrl
->GetWindowRect(ctrlRect
);
1503 return ctrlRect
.CenterPoint();