⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tooltips.c

📁 winNT技术操作系统,国外开放的原代码和LIUX一样
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * Tool tip control
 *
 * Copyright 1998, 1999 Eric Kohl
 * Copyright 2004 Robert Shearman
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 * NOTES
 *
 * This code was audited for completeness against the documented features
 * of Comctl32.dll version 6.0 on Sep. 08, 2004, by Robert Shearman.
 * 
 * Unless otherwise noted, we believe this code to be complete, as per
 * the specification mentioned above.
 * If you discover missing features or bugs please note them below.
 * 
 * TODO:
 *   - Custom draw support.
 *   - Animation.
 *   - Links.
 *   - Messages:
 *     o TTM_ADJUSTRECT
 *     o TTM_GETTITLEA
 *     o TTM_GETTTILEW
 *     o TTM_POPUP
 *   - Styles:
 *     o TTS_NOANIMATE
 *     o TTS_NOFADE
 *     o TTS_CLOSE
 *
 * Testing:
 *   - Run tests using Waite Group Windows95 API Bible Volume 2.
 *     The second cdrom (chapter 3) contains executables activate.exe,
 *     curtool.exe, deltool.exe, enumtools.exe, getinfo.exe, getiptxt.exe,
 *     hittest.exe, needtext.exe, newrect.exe, updtext.exe and winfrpt.exe.
 *
 *   Timer logic.
 *
 * One important point to remember is that tools don't necessarily get
 * a WM_MOUSEMOVE once the cursor leaves the tool, an example is when
 * a tool sets TTF_IDISHWND (i.e. an entire window is a tool) because
 * here WM_MOUSEMOVEs only get sent when the cursor is inside the
 * client area.  Therefore the only reliable way to know that the
 * cursor has left a tool is to keep a timer running and check the
 * position every time it expires.  This is the role of timer
 * ID_TIMERLEAVE.
 *
 *
 * On entering a tool (detected in a relayed WM_MOUSEMOVE) we start
 * ID_TIMERSHOW, if this times out and we're still in the tool we show
 * the tip.  On showing a tip we start both ID_TIMERPOP and
 * ID_TIMERLEAVE.  On hiding a tooltip we kill ID_TIMERPOP.
 * ID_TIMERPOP is restarted on every relayed WM_MOUSEMOVE.  If
 * ID_TIMERPOP expires the tool is hidden and ID_TIMERPOP is killed.
 * ID_TIMERLEAVE remains running - this is important as we need to
 * determine when the cursor leaves the tool.
 *
 * When ID_TIMERLEAVE expires or on a relayed WM_MOUSEMOVE if we're
 * still in the tool do nothing (apart from restart ID_TIMERPOP if
 * this is a WM_MOUSEMOVE) (ID_TIMERLEAVE remains running).  If we've
 * left the tool and entered another one then hide the tip and start
 * ID_TIMERSHOW with time ReshowTime and kill ID_TIMERLEAVE.  If we're
 * outside all tools hide the tip and kill ID_TIMERLEAVE.  On Relayed
 * mouse button messages hide the tip but leave ID_TIMERLEAVE running,
 * this again will let us keep track of when the cursor leaves the
 * tool.
 *
 *
 * infoPtr->nTool is the tool the mouse was on on the last relayed MM
 * or timer expiry or -1 if the mouse was not on a tool.
 *
 * infoPtr->nCurrentTool is the tool for which the tip is currently
 * displaying text for or -1 if the tip is not shown.  Actually this
 * will only ever be infoPtr-nTool or -1, so it could be changed to a
 * BOOL.
 *
 */



#include <stdarg.h>
#include <string.h>

#include "windef.h"
#include "winbase.h"
#include "wine/unicode.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "commctrl.h"
#include "comctl32.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(tooltips);

static HICON hTooltipIcons[TTI_ERROR+1];

typedef struct
{
    UINT      uFlags;
    HWND      hwnd;
    BOOL      bNotifyUnicode;
    UINT_PTR  uId;
    RECT      rect;
    HINSTANCE hinst;
    LPWSTR      lpszText;
    LPARAM      lParam;
} TTTOOL_INFO;


typedef struct
{
    WCHAR      szTipText[INFOTIPSIZE];
    BOOL     bActive;
    BOOL     bTrackActive;
    UINT     uNumTools;
    COLORREF   clrBk;
    COLORREF   clrText;
    HFONT    hFont;
    HFONT    hTitleFont;
    INT      xTrackPos;
    INT      yTrackPos;
    INT      nMaxTipWidth;
    INT      nTool; /* tool that mouse was on on last relayed mouse move */
    INT      nCurrentTool;
    INT      nTrackTool;
    INT      nReshowTime;
    INT      nAutoPopTime;
    INT      nInitialTime;
    RECT     rcMargin;
    BOOL     bToolBelow;
    LPWSTR   pszTitle;
    HICON    hTitleIcon;

    TTTOOL_INFO *tools;
} TOOLTIPS_INFO;

#define ID_TIMERSHOW   1    /* show delay timer */
#define ID_TIMERPOP    2    /* auto pop timer */
#define ID_TIMERLEAVE  3    /* tool leave timer */


#define TOOLTIPS_GetInfoPtr(hWindow) ((TOOLTIPS_INFO *)GetWindowLongPtrW (hWindow, 0))

/* offsets from window edge to start of text */
#define NORMAL_TEXT_MARGIN 2
#define BALLOON_TEXT_MARGIN (NORMAL_TEXT_MARGIN+8)
/* value used for CreateRoundRectRgn that specifies how much
 * each corner is curved */
#define BALLOON_ROUNDEDNESS 20
#define BALLOON_STEMHEIGHT 13
#define BALLOON_STEMWIDTH 10
#define BALLOON_STEMINDENT 20

#define BALLOON_ICON_TITLE_SPACING 8 /* horizontal spacing between icon and title */
#define BALLOON_TITLE_TEXT_SPACING 8 /* vertical spacing between icon/title and main text */
#define ICON_HEIGHT 16
#define ICON_WIDTH  16

static LRESULT CALLBACK
TOOLTIPS_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRef);


inline static UINT_PTR
TOOLTIPS_GetTitleIconIndex(HICON hIcon)
{
    UINT i;
    for (i = 0; i <= TTI_ERROR; i++)
        if (hTooltipIcons[i] == hIcon)
            return i;
    return (UINT_PTR)hIcon;
}

static void
TOOLTIPS_InitSystemSettings (TOOLTIPS_INFO *infoPtr)
{
    NONCLIENTMETRICSW nclm;

    infoPtr->clrBk   = GetSysColor (COLOR_INFOBK);
    infoPtr->clrText = GetSysColor (COLOR_INFOTEXT);

    DeleteObject (infoPtr->hFont);
    nclm.cbSize = sizeof(nclm);
    SystemParametersInfoW (SPI_GETNONCLIENTMETRICS, sizeof(nclm), &nclm, 0);
    infoPtr->hFont = CreateFontIndirectW (&nclm.lfStatusFont);

    DeleteObject (infoPtr->hTitleFont);
    nclm.lfStatusFont.lfWeight = FW_BOLD;
    infoPtr->hTitleFont = CreateFontIndirectW (&nclm.lfStatusFont);
}

static void
TOOLTIPS_Refresh (HWND hwnd, HDC hdc)
{
    TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr(hwnd);
    RECT rc;
    INT oldBkMode;
    HFONT hOldFont;
    HBRUSH hBrush;
    UINT uFlags = DT_EXTERNALLEADING;
    HRGN hRgn = NULL;
    DWORD dwStyle = GetWindowLongW(hwnd, GWL_STYLE);

    if (infoPtr->nMaxTipWidth > -1)
	uFlags |= DT_WORDBREAK;
    if (GetWindowLongW (hwnd, GWL_STYLE) & TTS_NOPREFIX)
	uFlags |= DT_NOPREFIX;
    GetClientRect (hwnd, &rc);

    hBrush = CreateSolidBrush(infoPtr->clrBk);

    oldBkMode = SetBkMode (hdc, TRANSPARENT);
    SetTextColor (hdc, infoPtr->clrText);

    if (dwStyle & TTS_BALLOON)
    {
        /* create a region to store result into */
        hRgn = CreateRectRgn(0, 0, 0, 0);

        GetWindowRgn(hwnd, hRgn);

        /* fill the background */
        FillRgn(hdc, hRgn, hBrush);
        DeleteObject(hBrush);
        hBrush = NULL;
    }
    else
    {
        /* fill the background */
        FillRect(hdc, &rc, hBrush);
        DeleteObject(hBrush);
        hBrush = NULL;
    }

    if ((dwStyle & TTS_BALLOON) || infoPtr->pszTitle)
    {
        /* calculate text rectangle */
        rc.left   += (BALLOON_TEXT_MARGIN + infoPtr->rcMargin.left);
        rc.top    += (BALLOON_TEXT_MARGIN + infoPtr->rcMargin.top);
        rc.right  -= (BALLOON_TEXT_MARGIN + infoPtr->rcMargin.right);
        rc.bottom -= (BALLOON_TEXT_MARGIN + infoPtr->rcMargin.bottom);
        if(infoPtr->bToolBelow) rc.top += BALLOON_STEMHEIGHT;

        if (infoPtr->pszTitle)
        {
            RECT rcTitle = {rc.left, rc.top, rc.right, rc.bottom};
            int height;
            BOOL icon_present;

            /* draw icon */
            icon_present = infoPtr->hTitleIcon && 
                DrawIconEx(hdc, rc.left, rc.top, infoPtr->hTitleIcon,
                           ICON_WIDTH, ICON_HEIGHT, 0, NULL, DI_NORMAL);
            if (icon_present)
                rcTitle.left += ICON_WIDTH + BALLOON_ICON_TITLE_SPACING;

            rcTitle.bottom = rc.top + ICON_HEIGHT;

            /* draw title text */
            hOldFont = SelectObject (hdc, infoPtr->hTitleFont);
            height = DrawTextW(hdc, infoPtr->pszTitle, -1, &rcTitle, DT_BOTTOM | DT_SINGLELINE | DT_NOPREFIX);
            SelectObject (hdc, hOldFont);
            rc.top += height + BALLOON_TITLE_TEXT_SPACING;
        }
    }
    else
    {
        /* calculate text rectangle */
        rc.left   += (NORMAL_TEXT_MARGIN + infoPtr->rcMargin.left);
        rc.top    += (NORMAL_TEXT_MARGIN + infoPtr->rcMargin.top);
        rc.right  -= (NORMAL_TEXT_MARGIN + infoPtr->rcMargin.right);
        rc.bottom -= (NORMAL_TEXT_MARGIN + infoPtr->rcMargin.bottom);
    }

    /* draw text */
    hOldFont = SelectObject (hdc, infoPtr->hFont);
    DrawTextW (hdc, infoPtr->szTipText, -1, &rc, uFlags);
    /* be polite and reset the things we changed in the dc */
    SelectObject (hdc, hOldFont);
    SetBkMode (hdc, oldBkMode);

    if (dwStyle & TTS_BALLOON)
    {
        /* frame region because default window proc doesn't do it */
        INT width = GetSystemMetrics(SM_CXDLGFRAME) - GetSystemMetrics(SM_CXEDGE);
        INT height = GetSystemMetrics(SM_CYDLGFRAME) - GetSystemMetrics(SM_CYEDGE);

        hBrush = GetSysColorBrush(COLOR_WINDOWFRAME);
        FrameRgn(hdc, hRgn, hBrush, width, height);
    }

    if (hRgn)
        DeleteObject(hRgn);
}

static void TOOLTIPS_GetDispInfoA(HWND hwnd, TOOLTIPS_INFO *infoPtr, TTTOOL_INFO *toolPtr)
{
    NMTTDISPINFOA ttnmdi;

    /* fill NMHDR struct */
    ZeroMemory (&ttnmdi, sizeof(NMTTDISPINFOA));
    ttnmdi.hdr.hwndFrom = hwnd;
    ttnmdi.hdr.idFrom = toolPtr->uId;
    ttnmdi.hdr.code = TTN_GETDISPINFOA;
    ttnmdi.lpszText = (LPSTR)&ttnmdi.szText;
    ttnmdi.uFlags = toolPtr->uFlags;
    ttnmdi.lParam = toolPtr->lParam;

    TRACE("hdr.idFrom = %x\n", ttnmdi.hdr.idFrom);
    SendMessageW(toolPtr->hwnd, WM_NOTIFY,
                 (WPARAM)toolPtr->uId, (LPARAM)&ttnmdi);

    if (IS_INTRESOURCE(ttnmdi.lpszText)) {
        LoadStringW(ttnmdi.hinst, LOWORD(ttnmdi.lpszText),
               infoPtr->szTipText, INFOTIPSIZE);
        if (ttnmdi.uFlags & TTF_DI_SETITEM) {
            toolPtr->hinst = ttnmdi.hinst;
            toolPtr->lpszText = (LPWSTR)ttnmdi.lpszText;
        }
    }
    else if (ttnmdi.lpszText == 0) {
        /* no text available */
        infoPtr->szTipText[0] = '\0';
    }
    else if (ttnmdi.lpszText != LPSTR_TEXTCALLBACKA) {
        INT max_len = (ttnmdi.lpszText == &ttnmdi.szText[0]) ? 
                sizeof(ttnmdi.szText)/sizeof(ttnmdi.szText[0]) : -1;
        MultiByteToWideChar(CP_ACP, 0, ttnmdi.lpszText, max_len,
                            infoPtr->szTipText, INFOTIPSIZE);
        if (ttnmdi.uFlags & TTF_DI_SETITEM) {
            INT len = MultiByteToWideChar(CP_ACP, 0, ttnmdi.lpszText,
					  max_len, NULL, 0);
            toolPtr->hinst = 0;
            toolPtr->lpszText =	Alloc (len * sizeof(WCHAR));
            MultiByteToWideChar(CP_ACP, 0, ttnmdi.lpszText, -1,
                                toolPtr->lpszText, len);
        }
    }
    else {
        ERR("recursive text callback!\n");
        infoPtr->szTipText[0] = '\0';
    }
}

static void TOOLTIPS_GetDispInfoW(HWND hwnd, TOOLTIPS_INFO *infoPtr, TTTOOL_INFO *toolPtr)
{
    NMTTDISPINFOW ttnmdi;

    /* fill NMHDR struct */
    ZeroMemory (&ttnmdi, sizeof(NMTTDISPINFOW));
    ttnmdi.hdr.hwndFrom = hwnd;
    ttnmdi.hdr.idFrom = toolPtr->uId;
    ttnmdi.hdr.code = TTN_GETDISPINFOW;
    ttnmdi.lpszText = (LPWSTR)&ttnmdi.szText;
    ttnmdi.uFlags = toolPtr->uFlags;
    ttnmdi.lParam = toolPtr->lParam;

    TRACE("hdr.idFrom = %x\n", ttnmdi.hdr.idFrom);
    SendMessageW(toolPtr->hwnd, WM_NOTIFY,
                 (WPARAM)toolPtr->uId, (LPARAM)&ttnmdi);

    if (IS_INTRESOURCE(ttnmdi.lpszText)) {
        LoadStringW(ttnmdi.hinst, LOWORD(ttnmdi.lpszText),
               infoPtr->szTipText, INFOTIPSIZE);
        if (ttnmdi.uFlags & TTF_DI_SETITEM) {
            toolPtr->hinst = ttnmdi.hinst;
            toolPtr->lpszText = ttnmdi.lpszText;
        }
    }
    else if (ttnmdi.lpszText == 0) {
        /* no text available */
        infoPtr->szTipText[0] = '\0';
    }
    else if (ttnmdi.lpszText != LPSTR_TEXTCALLBACKW) {
        INT max_len = (ttnmdi.lpszText == &ttnmdi.szText[0]) ? 
                sizeof(ttnmdi.szText)/sizeof(ttnmdi.szText[0]) : INFOTIPSIZE-1;
        lstrcpynW(infoPtr->szTipText, ttnmdi.lpszText, max_len);
        if (ttnmdi.uFlags & TTF_DI_SETITEM) {
            INT len = max(strlenW(ttnmdi.lpszText), max_len);
            toolPtr->hinst = 0;
            toolPtr->lpszText =	Alloc ((len+1) * sizeof(WCHAR));
            memcpy(toolPtr->lpszText, ttnmdi.lpszText, (len+1) * sizeof(WCHAR));
        }
    }
    else {
        ERR("recursive text callback!\n");
        infoPtr->szTipText[0] = '\0';
    }
}

static void
TOOLTIPS_GetTipText (HWND hwnd, TOOLTIPS_INFO *infoPtr, INT nTool)
{
    TTTOOL_INFO *toolPtr = &infoPtr->tools[nTool];

    if (IS_INTRESOURCE(toolPtr->lpszText) && toolPtr->hinst) {
	/* load a resource */
	TRACE("load res string %p %x\n",
	       toolPtr->hinst, LOWORD(toolPtr->lpszText));
	LoadStringW (toolPtr->hinst, LOWORD(toolPtr->lpszText),
		       infoPtr->szTipText, INFOTIPSIZE);
    }
    else if (toolPtr->lpszText) {
	if (toolPtr->lpszText == LPSTR_TEXTCALLBACKW) {
	    if (toolPtr->bNotifyUnicode)
		TOOLTIPS_GetDispInfoW(hwnd, infoPtr, toolPtr);
	    else
		TOOLTIPS_GetDispInfoA(hwnd, infoPtr, toolPtr);
	}
	else {
	    /* the item is a usual (unicode) text */
	    lstrcpynW (infoPtr->szTipText, toolPtr->lpszText, INFOTIPSIZE);
	}
    }
    else {
	/* no text available */
	infoPtr->szTipText[0] = L'\0';
    }

    TRACE("%s\n", debugstr_w(infoPtr->szTipText));
}


static void
TOOLTIPS_CalcTipSize (HWND hwnd, TOOLTIPS_INFO *infoPtr, LPSIZE lpSize)
{
    HDC hdc;
    HFONT hOldFont;
    DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
    UINT uFlags = DT_EXTERNALLEADING | DT_CALCRECT;
    RECT rc = {0, 0, 0, 0};
    SIZE title = {0, 0};

    if (infoPtr->nMaxTipWidth > -1) {
	rc.right = infoPtr->nMaxTipWidth;
	uFlags |= DT_WORDBREAK;
    }
    if (style & TTS_NOPREFIX)
	uFlags |= DT_NOPREFIX;
    TRACE("%s\n", debugstr_w(infoPtr->szTipText));

    hdc = GetDC (hwnd);
    if (infoPtr->pszTitle)
    {
        RECT rcTitle = {0, 0, 0, 0};
        TRACE("title %s\n", debugstr_w(infoPtr->pszTitle));
        if (infoPtr->hTitleIcon)
        {
            title.cx = ICON_WIDTH;
            title.cy = ICON_HEIGHT;
        }
        if (title.cx != 0) title.cx += BALLOON_ICON_TITLE_SPACING;
        hOldFont = SelectObject (hdc, infoPtr->hTitleFont);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -