// // balloon.cpp // #include "private.h" #include "globals.h" #include "tipbar.h" #include "helpers.h" #include "xstring.h" #include "immxutil.h" #include "balloon.h" #include "fontlink.h" #include "asynccal.h" extern CTipbarWnd *g_pTipbarWnd; #define SHOWTOOLTIP_ONUPDATE __inline void PrectSet(RECT *prc, int left, int top, int right, int bottom) { prc->left = left; prc->top = top; prc->right = right; prc->bottom = bottom; } ////////////////////////////////////////////////////////////////////////////// // // CTipbarBalloonItem // ////////////////////////////////////////////////////////////////////////////// //+--------------------------------------------------------------------------- // // IUnknown // //---------------------------------------------------------------------------- STDAPI CTipbarBalloonItem::QueryInterface(REFIID riid, void **ppvObj) { *ppvObj = NULL; if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfLangBarItemSink)) { *ppvObj = SAFECAST(this, ITfLangBarItemSink *); } else if (IsEqualIID(riid, IID_PRIV_BALLOONITEM)) { *ppvObj = this; } if (*ppvObj) { AddRef(); return S_OK; } return E_NOINTERFACE; } STDAPI_(ULONG) CTipbarBalloonItem::AddRef() { return ++_cRef; } STDAPI_(ULONG) CTipbarBalloonItem::Release() { _cRef--; Assert(_cRef >= 0); if (_cRef == 0) { delete this; return 0; } return _cRef; } //+--------------------------------------------------------------------------- // // ctor // //---------------------------------------------------------------------------- CTipbarBalloonItem::CTipbarBalloonItem(CTipbarThread *ptt, ITfLangBarItem *plbi, ITfLangBarItemBalloon *plbiBalloon, DWORD dwId, RECT *prc, DWORD dwStyle, TF_LANGBARITEMINFO *plbiInfo, DWORD dwStatus) : CUIFObject(ptt->_ptw, dwId, prc, dwStyle) , CTipbarItem(ptt, plbi, plbiInfo, dwStatus) { Dbg_MemSetThisName(TEXT("CTipbarBalloonItem")); _plbiBalloon = plbiBalloon; _plbiBalloon->AddRef(); if (_dwStatus & TF_LBI_STATUS_DISABLED) Enable(FALSE); _cRef = 1; } //+--------------------------------------------------------------------------- // // dtor // //---------------------------------------------------------------------------- CTipbarBalloonItem::~CTipbarBalloonItem() { if (_bstrText) SysFreeString(_bstrText); SafeRelease(_plbiBalloon); } //+--------------------------------------------------------------------------- // // Update // //---------------------------------------------------------------------------- HRESULT CTipbarBalloonItem::OnUpdateHandler(DWORD dwFlags, DWORD dwStatus) { if (!IsConnected()) return S_OK; HRESULT hr = S_OK; BOOL fCallPaint = FALSE; // // add ref count to be safe for releasing during marshaling. // AddRef(); if (dwFlags & TF_LBI_BALLOON) { TF_LBBALLOONINFO info; if (_bstrText) { SysFreeString(_bstrText); _bstrText = NULL; } if (SUCCEEDED(_plbiBalloon->GetBalloonInfo(&info))) { _bstrText = info.bstrText; _style = info.style; } if (!IsHiddenStatusControl() && IsVisibleInToolbar()) StartDemotingTimer(FALSE); EndTimer(); DestroyBalloonTip(); #ifdef SHOWTOOLTIP_ONUPDATE if (IsTextEllipsis(_bstrText, &GetRectRef())) { StartTimer(2000); } #endif fCallPaint = TRUE; } CTipbarItem::OnUpdateHandler(dwFlags, dwStatus); if ((_dwStatus & TF_LBI_STATUS_DISABLED) || (_dwStatus & TF_LBI_STATUS_HIDDEN)) { DestroyBalloonTip(); } if (fCallPaint) CallOnPaint(); Release(); return hr; } //+--------------------------------------------------------------------------- // // OnTimer // //---------------------------------------------------------------------------- void CTipbarBalloonItem::OnTimer() { if (!_pblnTip) { EndTimer(); StartTimer(20000); ShowBalloonTip(); } else { EndTimer(); DestroyBalloonTip(); } } //---------------------------------------------------------------------------- // // OnPosChanged // //---------------------------------------------------------------------------- void CTipbarBalloonItem::OnPosChanged() { EndTimer(); DestroyBalloonTip(); } //+--------------------------------------------------------------------------- // // ShowBalloonTip // //---------------------------------------------------------------------------- void CTipbarBalloonItem::ShowBalloonTip() { RECT rc; POINT pt; DestroyBalloonTip(); if (!_ptt) return; if (!_ptt->_ptw) return; if (!IsVisibleInToolbar() || !_ptt->IsFocusThread()) return; _pblnTip = new CUIFBalloonWindow(g_hInst, 0); if (!_pblnTip) return; _pblnTip->Initialize(); GetRect(&rc); pt.x = (rc.left + rc.right) / 2; pt.y = rc.top; MyClientToScreen(&pt, &rc); _pblnTip->SetTargetPos(pt); _pblnTip->SetExcludeRect(&rc); _pblnTip->SetText(_bstrText); _pblnTip->CreateWnd(_ptt->_ptw->GetWnd()); } //+--------------------------------------------------------------------------- // // DestroyBalloonTip // //---------------------------------------------------------------------------- void CTipbarBalloonItem::DestroyBalloonTip() { if (_pblnTip) { if (IsWindow(_pblnTip->GetWnd())) DestroyWindow(_pblnTip->GetWnd()); delete _pblnTip; _pblnTip = NULL; } } //+--------------------------------------------------------------------------- // // OnPaint // //---------------------------------------------------------------------------- void CTipbarBalloonItem::OnPaint( HDC hdc ) { switch (_style) { case TF_LB_BALLOON_RECO: DrawRecoBalloon(hdc, _bstrText, &GetRectRef()); break; case TF_LB_BALLOON_SHOW: DrawShowBalloon(hdc, _bstrText, &GetRectRef()); break; case TF_LB_BALLOON_MISS: DrawUnrecognizedBalloon(hdc, _bstrText, &GetRectRef()); break; default: Assert(0); } } //+--------------------------------------------------------------------------- // // OnRightClick // //---------------------------------------------------------------------------- void CTipbarBalloonItem::OnRightClick() { if (_plbiBalloon) { HRESULT hr; POINT pt; RECT rc; GetCursorPos(&pt); GetRect(&rc); MyClientToScreen(&rc); CAsyncCall *pac = new CAsyncCall(_plbiBalloon); if (pac) { hr = pac->OnClick(TF_LBI_CLK_RIGHT, pt, &rc); pac->_Release(); } else { hr = E_OUTOFMEMORY; } if (RPC_S_SERVER_UNAVAILABLE == HRESULT_CODE(hr)) { if (_ptt && _ptt->_ptw) _ptt->_ptw->OnThreadTerminate(_ptt->_dwThreadId); return; } } } //+--------------------------------------------------------------------------- // // OnLeftClick // //---------------------------------------------------------------------------- void CTipbarBalloonItem::OnLeftClick() { if (_plbiBalloon) { HRESULT hr; POINT pt; RECT rc; GetCursorPos(&pt); GetRect(&rc); MyClientToScreen(&rc); CAsyncCall *pac = new CAsyncCall(_plbiBalloon); if (pac) { hr = pac->OnClick(TF_LBI_CLK_LEFT, pt, &rc); pac->_Release(); } else { hr = E_OUTOFMEMORY; } if (RPC_S_SERVER_UNAVAILABLE == HRESULT_CODE(hr)) { if (_ptt && _ptt->_ptw) _ptt->_ptw->OnThreadTerminate(_ptt->_dwThreadId); return; } if (!IsHiddenStatusControl() && IsVisibleInToolbar()) StartDemotingTimer(TRUE); } } //+--------------------------------------------------------------------------- // // SetRect // //---------------------------------------------------------------------------- void CTipbarBalloonItem::SetRect( const RECT *prc ) { CUIFObject::SetRect(prc); } /*--------------------------------------------------------------------------- DrawTransparentText Draws the text string wtz in the font ft with text color crText. ------------------------------------------------------------------- TCOON -*/ void CTipbarBalloonItem::DrawTransparentText(HDC hdc, COLORREF crText, WCHAR *psz, const RECT *prc) { HFONT hFontOld; SIZE size; int cch; RECT rc; WORD rgfJustify = DT_END_ELLIPSIS | DT_WORDBREAK | DT_EDITCONTROL | DT_CENTER | DT_VCENTER; if (!psz || !_ptt || !_ptt->_ptw) return; cch = wcslen(psz); // prepare objects hFontOld= (HFONT)SelectObject(hdc, GetFont() ); // calc alignment FLGetTextExtentPoint32( hdc, psz, cch, &size ); COLORREF crSave = SetTextColor(hdc, crText); SetBkMode(hdc, TRANSPARENT); if (!_ptt->IsVertical()) { int yAlign = (prc->bottom - prc->top - size.cy) / 2; rc = *prc; rc.left += 1; rc.right -= 1; rc.top += yAlign; FLDrawTextW(hdc, psz, cch, &rc, rgfJustify); } else { int xAlign = (prc->right - prc->left - size.cy) / 2; rc = *prc; rc.top += 1; rc.bottom -= 1; rc.right -= xAlign; FLDrawTextWVert(hdc, psz, cch, &rc, rgfJustify); } SetTextColor(hdc, crSave); SelectObject(hdc, hFontOld); } /*--------------------------------------------------------------------------- DrawRect Draws the text string wtz in the font ft with text color crText. ------------------------------------------------------------------- TCOON -*/ void CTipbarBalloonItem::DrawRect(HDC hdc, const RECT *prc, COLORREF crBorder, COLORREF crFill) { HPEN hpen = NULL; HPEN hpenOld; HBRUSH hbr = NULL; HBRUSH hbrOld; hpen = CreatePen(PS_SOLID, 0, crBorder); if (!hpen) goto Exit; hbr = CreateSolidBrush(crFill); if (!hbr) goto Exit; hpenOld = (HPEN)SelectObject(hdc, hpen); hbrOld = (HBRUSH)SelectObject(hdc, hbr); Rectangle(hdc, prc->left, prc->top, prc->right, prc->bottom); SelectObject(hdc, hpenOld); SelectObject(hdc, hbrOld); Exit: if (hpen) DeleteObject(hpen); if (hbr) DeleteObject(hbr); } /*--------------------------------------------------------------------------- DrawUnrecognizedBalloon Draws the Unrecognized balloon to show the user that the command was misunderstood. ------------------------------------------------------------------- TCOON -*/ void CTipbarBalloonItem::DrawUnrecognizedBalloon(HDC hdc, WCHAR *wtz, const RECT *prc) { RECT rc = *prc; COLORREF crBtnText; HPEN hpen = NULL; HPEN hpenOld; HBRUSH hbrOld; crBtnText = GetSysColor(COLOR_BTNTEXT); hpen = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_WINDOWFRAME)); if (!hpen) goto Exit; hbrOld = (HBRUSH)SelectObject(hdc, GetSysColorBrush(COLOR_HIGHLIGHT)); InflateRect(&rc, -2, -2); hpenOld = (HPEN)SelectObject(hdc, hpen); RoundRect(hdc, rc.left,rc.top, rc.right, rc.bottom, 12, 12); DrawTransparentText(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT), wtz, &rc); // Restore DC contents SelectObject(hdc, hpenOld); SelectObject(hdc, hbrOld); Exit: if (hpen) DeleteObject(hpen); } /*--------------------------------------------------------------------------- DrawShowBalloon Draws the Show balloon to suggest the user say a given command. ------------------------------------------------------------------- TCOON -*/ void CTipbarBalloonItem::DrawShowBalloon(HDC hdc, WCHAR *wtz, const RECT *prc) { COLORREF crBtnFace; COLORREF crBtnShad; COLORREF crBalloonText; COLORREF crBalloon; RECT rc = *prc; crBtnFace = GetSysColor(COLOR_BTNFACE); crBtnShad = GetSysColor(COLOR_BTNSHADOW); // crBalloonText = GetSysColor(COLOR_INFOTEXT); // crBalloon = GetSysColor(COLOR_INFOBK) & 0x0000ff00; // crBalloon = col(30, RGB(0, 0xFF, 0), 70, GetSysColor(COLOR_INFOBK)); crBalloon = GetSysColor(COLOR_BTNFACE); crBalloonText = GetSysColor(COLOR_BTNTEXT); // Draw Black outline InflateRect(&rc, -2, -2); // rc.bottom -= 5; DrawRect(hdc, &rc, GetSysColor(COLOR_WINDOWFRAME), crBalloon); InflateRect(&rc, -1, -1); // Knock off the corner RECT rcDraw; int bkSav = SetBkColor(hdc, crBalloon); for (int iPixel = 0; iPixel < 7; iPixel++) { PrectSet(&rcDraw, rc.right-(7-iPixel), rc.bottom-iPixel-1, rc.right-(7-iPixel)+1, rc.bottom-iPixel); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); } PrectSet(&rcDraw, rc.right-3, rc.bottom-2, rc.right-2, rc.bottom); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); PrectSet(&rcDraw, rc.right-2, rc.bottom-3, rc.right, rc.bottom-2); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); // Shadows SetBkColor(hdc, crBtnShad); // Lower Shadow PrectSet(&rcDraw, rc.left+2, rc.bottom+1, rc.right+1, rc.bottom+2); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); for (iPixel = 0; iPixel < 8; iPixel++) { PrectSet(&rcDraw, rc.right-(7-iPixel), rc.bottom-iPixel, rc.right-(7-iPixel)+1, rc.bottom-iPixel+2); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); } // Button Face portions SetBkColor(hdc, crBtnFace); for (iPixel = 0; iPixel < 3; iPixel++) { PrectSet(&rcDraw, rc.right-6+iPixel, rc.bottom+1-iPixel, rc.right-3, rc.bottom-iPixel+2); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); } for (iPixel = 0; iPixel < 2; iPixel++) { PrectSet(&rcDraw, rc.right+iPixel-1, rc.bottom-4-iPixel, rc.right+iPixel, rc.bottom+iPixel-3); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); } PrectSet(&rcDraw, rc.right-3, rc.bottom, rc.right-2, rc.bottom+2); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); // Clean up and draw text SetBkColor(hdc, bkSav); DrawTransparentText(hdc, crBalloonText, wtz, &rc); } /*--------------------------------------------------------------------------- DrawRecoBalloon Draws the Recognition Balloon. ------------------------------------------------------------------- TCOON -*/ void CTipbarBalloonItem::DrawRecoBalloon(HDC hdc, WCHAR *wtz, const RECT *prc) { COLORREF crBtnFace; COLORREF crBtnShad; COLORREF crBalloonText; COLORREF crBalloon; RECT rc = *prc; crBtnFace = GetSysColor(COLOR_BTNFACE); crBtnShad = GetSysColor(COLOR_BTNSHADOW); crBalloonText = GetSysColor(COLOR_INFOTEXT); crBalloon = GetSysColor(COLOR_INFOBK); // Draw Black outline InflateRect(&rc, -2, -2); // rc.bottom -= 5; rc.bottom -= 1; DrawRect(hdc, &rc, GetSysColor(COLOR_WINDOWFRAME), crBalloon); InflateRect(&rc, -1, -1); // Knock off the corners int bkSav = SetBkColor(hdc, crBtnFace); InflateRect(&rc, +1, +1); RECT rcDraw; // UpperLeft PrectSet(&rcDraw, rc.left, rc.top, rc.left+1, rc.top+1); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); // UpperRight PrectSet(&rcDraw, rc.right-1, rc.top, rc.right, rc.top+1); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); // LowerLeft PrectSet(&rcDraw, rc.left, rc.bottom-1, rc.left+1, rc.bottom); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); SetBkColor(hdc, crBtnShad); // LowerRight (done in shadow color) PrectSet(&rcDraw, rc.right-1, rc.bottom-1, rc.right, rc.bottom); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); // Lower Shadow PrectSet(&rcDraw, rc.left+2, rc.bottom, rc.right-1, rc.bottom+1); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); // RightSide Shadow PrectSet(&rcDraw, rc.right, rc.top+2, rc.right+1, rc.bottom-1); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); // Arrow Shadow PrectSet(&rcDraw, rc.right-4, rc.bottom+1, rc.right-3, rc.bottom+5); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); // Arrow SetBkColor(hdc, crBalloonText); PrectSet(&rcDraw, rc.right-5, rc.bottom, rc.right-4, rc.bottom+4); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); int iPixel; for (iPixel = 0; iPixel < 3; iPixel++) { PrectSet(&rcDraw, rc.right-(6+iPixel), rc.bottom+(2-iPixel), rc.right-(5+iPixel), rc.bottom+(3-iPixel)); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); } // Fill in Arrow SetBkColor(hdc, crBalloon); for (iPixel = 0; iPixel < 3; iPixel++) { PrectSet(&rcDraw, rc.right-(8-iPixel), rc.bottom-(1-iPixel), rc.right-5, rc.bottom+iPixel); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, 0); } SetBkColor(hdc, bkSav); DrawTransparentText(hdc, crBalloonText, wtz, &rc); } /*--------------------------------------------------------------------------- IsTextEllipsis Draws the text string wtz in the font ft with text color crText. ------------------------------------------------------------------- TCOON -*/ BOOL CTipbarBalloonItem::IsTextEllipsis(WCHAR *psz, const RECT *prc) { HDC hdc; HFONT hFontOld; SIZE size; int cch; RECT rc; BOOL bRet = FALSE; // detect if the item is hidden and return immediately here if (IsInHiddenStatus()) return bRet; if (g_pTipbarWnd && g_pTipbarWnd->IsSFHidden( )) return bRet; if (!psz) return bRet; hdc = CreateIC("DISPLAY", NULL, NULL, NULL); if (!hdc) return bRet; cch = wcslen(psz); // prepare objects hFontOld= (HFONT)SelectObject(hdc, GetFont() ); // calc alignment FLGetTextExtentPoint32( hdc, psz, cch, &size ); rc = *prc; rc.left += 3; rc.right -= 3; SelectObject(hdc, hFontOld); DeleteDC(hdc); return (size.cx > (rc.right - rc.left)) ? TRUE : FALSE; }