//----------------------------------------------------------------------------- // File: flextooltip.cpp // // Desc: Implements a tooltip class that displays a text string as a tooltip. // CFlexTooltip (derived from CFlexWnd) is used throughout the UI when // a control needs to have a tooltip. // // Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved. //----------------------------------------------------------------------------- #include "common.hpp" UINT_PTR CFlexToolTip::s_uiTimerID; DWORD CFlexToolTip::s_dwLastTimeStamp; // Last time stamp for mouse move TOOLTIPINIT CFlexToolTip::s_TTParam; // Parameters to initialize the tooltip // TimerFunc is called periodically. It checks if a tooltip should be displayed. // If a window has indicated a need for tooltip, TimerFunc will initialize // for displaying here. CFlexWnd will do the actual displaying, since // it has to monitor WM_MOUSEMOVE message to be sure that tooltips only // display after a period of inactivity. void CALLBACK CFlexToolTip::TimerFunc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { // If it has been one sec already since last mouse move, display the tooltip if (dwTime > CFlexWnd::s_dwLastMouseMove + 1000) { if (s_TTParam.hWndParent && !CFlexWnd::s_ToolTip.IsEnabled()) { // Check if the mouse cursor is outside the window. If so, don't activate tooltip. POINT pt; RECT rect; GetCursorPos(&pt); ScreenToClient(s_TTParam.hWndParent, &pt); ::GetClientRect(s_TTParam.hWndParent, &rect); if (!PtInRect(&rect, pt)) return; SetParent(CFlexWnd::s_ToolTip.m_hWnd, s_TTParam.hWndParent); CFlexWnd::s_ToolTip.SetSBWidth(s_TTParam.iSBWidth); CFlexWnd::s_ToolTip.SetID(s_TTParam.dwID); CFlexWnd::s_ToolTip.SetNotifyWindow(s_TTParam.hWndNotify); CFlexWnd::s_ToolTip.SetText(s_TTParam.tszCaption); CFlexWnd::s_ToolTip.SetEnable(TRUE); } } } // We use the constructor and destructor to load and unload WINMM.DLL since the UI will only create this once. CFlexToolTip::CFlexToolTip() : m_tszText(NULL), m_hNotifyWnd(NULL), m_dwID(0), m_bEnabled(FALSE) { } CFlexToolTip::~CFlexToolTip() { delete[] m_tszText; } HWND CFlexToolTip::Create(HWND hParent, const RECT &rect, BOOL bVisible, int iSBWidth) { m_iSBWidth = iSBWidth; return CFlexWnd::Create(hParent, rect, bVisible); } // Set the tooltip position. pt is the upper-left point in screen coordinates. // bOffsetForMouseCursor is TRUE if the tooltip is to be displayed next to mouse cursor. SetPosition() // will offset the position of the tooltip so that the cursor doesn't block the text of the tooltip. void CFlexToolTip::SetPosition(POINT pt, BOOL bOffsetForMouseCursor) { // Check the top, right and bottom edges. If they are outside the main config window RECT rc; RECT cliprc; RECT parentrc; GetWindowRect(GetParent(), &parentrc); GetClientRect(&rc); GetWindowRect(GetParent(), &cliprc); cliprc.right -= DEFAULTVIEWSBWIDTH*2; if (bOffsetForMouseCursor) { pt.y -= rc.bottom; // Align the lower left corner to the cursor pt.x += 1; pt.y -= 1; // Offset x and y by 2 pixels so that when the mouse is moved up or right, we don't get over the tooltip window. } if (pt.y < cliprc.top) pt.y += cliprc.top - pt.y; // Top if (pt.x + rc.right > (cliprc.right - m_iSBWidth)) pt.x += cliprc.right - m_iSBWidth - (pt.x + rc.right); // Right if (pt.y + rc.bottom > cliprc.bottom) pt.y += cliprc.bottom - (pt.y + rc.bottom); // Bottom ScreenToClient(GetParent(), &pt); SetWindowPos(m_hWnd, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE); } void CFlexToolTip::SetText(LPCTSTR tszText, POINT *textpos) { // Figure out window size and position RECT rc = {0, 0, 320, 480}; // Only go to half the window width max HDC hDC = CreateCompatibleDC(NULL); if (hDC != NULL) { DrawText(hDC, CFlexToolTip::s_TTParam.tszCaption, -1, &rc, DT_CALCRECT); // DrawText(hDC, m_tszText, -1, &rc, DT_CALCRECT); SetWindowPos(m_hWnd, HWND_TOP, 0, 0, rc.right, rc.bottom, 0); // Set window pos as needed by text DeleteDC(hDC); } POINT pt; if (textpos) { pt = *textpos; SetPosition(pt, FALSE); } else { GetCursorPos(&pt); SetPosition(pt); } SetWindowPos(m_hWnd, HWND_TOP, 0, 0, rc.right, rc.bottom, SWP_NOMOVE); // Set size } void CFlexToolTip::OnClick(POINT point, WPARAM fwKeys, BOOL bLeft) { ClientToScreen(m_hWnd, &point); ScreenToClient(m_hNotifyWnd, &point); if (bLeft) PostMessage(m_hNotifyWnd, WM_LBUTTONDOWN, fwKeys, point.x | (point.y << 16)); else PostMessage(m_hNotifyWnd, WM_RBUTTONDOWN, fwKeys, point.x | (point.y << 16)); } void CFlexToolTip::OnDoubleClick(POINT point, WPARAM fwKeys, BOOL bLeft) { ClientToScreen(m_hWnd, &point); ScreenToClient(m_hNotifyWnd, &point); if (bLeft) PostMessage(m_hNotifyWnd, WM_LBUTTONDBLCLK, fwKeys, point.x | (point.y << 16)); else PostMessage(m_hNotifyWnd, WM_RBUTTONDBLCLK, fwKeys, point.x | (point.y << 16)); } void CFlexToolTip::OnPaint(HDC hDC) { HDC hBDC = NULL, hODC = NULL; CBitmap *pbm = NULL; if (!InRenderMode()) { hODC = hDC; pbm = CBitmap::Create(GetClientSize(), RGB(0,0,0), hDC); if (pbm != NULL) { hBDC = pbm->BeginPaintInto(); if (hBDC != NULL) { hDC = hBDC; } } } InternalPaint(hDC); if (!InRenderMode()) { if (pbm != NULL) { if (hBDC != NULL) { pbm->EndPaintInto(hBDC); pbm->Draw(hODC); } delete pbm; } } } void CFlexToolTip::InternalPaint(HDC hDC) { HGDIOBJ hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbBk); if (hPen != NULL) { HGDIOBJ hOldPen = SelectObject(hDC, hPen); HGDIOBJ hBrush = CreateSolidBrush(m_rgbBk); if (hBrush != NULL) { HGDIOBJ hOldBrush = SelectObject(hDC, hBrush); RECT rc = {0,0,0,0}; GetClientRect(&rc); DrawText(hDC, CFlexToolTip::s_TTParam.tszCaption, -1, &rc, DT_LEFT); SelectObject(hDC, hOldBrush); DeleteObject(hBrush); } SelectObject(hDC, hOldPen); DeleteObject(hPen); } } LRESULT CFlexToolTip::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { return CFlexWnd::WndProc(hWnd, msg, wParam, lParam); } LRESULT CFlexToolTip::OnCreate(LPCREATESTRUCT lpCreateStruct) { // Create a timer CFlexToolTip::s_uiTimerID = SetTimer(m_hWnd, 6, 50, CFlexToolTip::TimerFunc); return 0; } void CFlexToolTip::OnDestroy() { // Kill the timer if (CFlexToolTip::s_uiTimerID) { KillTimer(m_hWnd, 6); CFlexToolTip::s_uiTimerID = 0; } }