//+----------------------------------------------------------------------------
//
// File:     tooltip.cpp
//
// Module:   CMDIAL32.DLL
//
// Synopsis: This module contains the code for the implementing balloon tips.
//
// Copyright (c) 1996-2000 Microsoft Corporation
//
// Author:   markcl    Created Header   11/2/00
//
//+----------------------------------------------------------------------------

#include "cmmaster.h"


WNDPROC CBalloonTip::m_pfnOrgBalloonWndProc = NULL;

//+----------------------------------------------------------------------------
//
// Function:  CBalloonTip::CBalloonTip
//
// Synopsis:  Balloon tip constructor
//
// Arguments:	nothing
//
// Returns:   nothing
//
// History:   markcl	Created Header    10/31/00
//
//+----------------------------------------------------------------------------

CBalloonTip::CBalloonTip()
{
    // Nothing to do
}

//+----------------------------------------------------------------------------
//
// Function:  CBalloonTip::DisplayBallonTip
//
// Synopsis:  Displays a balloon tip
//
// Arguments:	LLPOINT	lppoint         - pointer to a POINT struct with the coordinates for displaying
//              int		iIcon           - type of icon to display in the balloon tip
//              LPCTSTR	lpszTitle       - Title of the balloon tip window
//              LPTSTR	lpszBalloonMsg  - Message to display in the balloon tip
//              HWND	hWndParent      - handle to the parent window
//
// Returns:   nothing
//
// History:   markcl    Created Header    10/31/00
//
//+----------------------------------------------------------------------------
BOOL CBalloonTip::DisplayBalloonTip(LPPOINT lppoint, UINT iIcon, LPCTSTR lpszTitle, LPTSTR lpszBalloonMsg, HWND hWndParent)
{

    //
    //	If we don't have a message or a position, we don't display the balloon tip.
    //
    if (NULL == lpszBalloonMsg || NULL == lppoint)
    {
        MYDBGASSERT(lpszBalloonMsg && lppoint);

        return FALSE;
    }

    //
    //  Comctl32.dll must be 5.80 or greater to use balloon tips.  We check the dll version 
    //  by calling DllGetVersion in comctl32.dll.
    //
    HINSTANCE hComCtl = LoadLibraryExA("comctl32.dll", NULL, 0);

    CMASSERTMSG(hComCtl, TEXT("LoadLibrary - comctl32 failed for Balloon Tips"));

    if (hComCtl != NULL)
    {
        typedef HRESULT (*DLLGETVERSIONPROC)(DLLVERSIONINFO* lpdvi);

        DLLGETVERSIONPROC fnDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hComCtl,"DllGetVersion");

        MYDBGASSERT(fnDllGetVersion);

        if (NULL == fnDllGetVersion)
        {
            //
            //  DllGetVersion does not exist in Comctl32.dll.  This mean the version is too old so we need to fail.
            //
            FreeLibrary(hComCtl);
            return FALSE;
        }
        else
        {
            DLLVERSIONINFO dvi;

            ZeroMemory(&dvi, sizeof(dvi));
            dvi.cbSize = sizeof(dvi);

            HRESULT hResult = (*fnDllGetVersion)(&dvi);

            FreeLibrary(hComCtl);
			
            if (SUCCEEDED(hResult))
            {
                //
                //  Take the version returned and compare it to 5.80.
                //
                if (MAKELONG(dvi.dwMinorVersion,dvi.dwMajorVersion) < MAKELONG(80,5))
                {
                    CMTRACE2(TEXT("COMCTL32.DLL version - %d.%d"),dvi.dwMajorVersion,dvi.dwMinorVersion);
                    CMTRACE1(TEXT("COMCTL32.DLL MAKELONG - %li"),MAKELONG(dvi.dwMinorVersion,dvi.dwMajorVersion));
                    CMTRACE1(TEXT("Required minimum MAKELONG - %li"),MAKELONG(80,5));
					
                    // Wrong DLL version
                    return FALSE;
                }
            }
            else
            {
                CMASSERTMSG(FALSE, TEXT("Call to DllGetVersion in comctl32.dll failed."));
                return FALSE;
            }
        }
    }

    //
    //  Hide any existing balloon tips before trying to display a new one.
    //    
    if (m_hwndTT && m_bTTActive)
    {
        HideBalloonTip();
    }

    //
    // Create the Balloon ToolTip window
    //
    m_hwndTT = CreateWindowExU(NULL,TOOLTIPS_CLASS, TEXT("CM Balloon Tip Window"),
                               WS_POPUP | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT,
                               CW_USEDEFAULT, CW_USEDEFAULT, hWndParent, NULL, g_hInst, NULL);

    if (m_hwndTT)
    {
        m_ti.cbSize = sizeof(m_ti);
        m_ti.uFlags = TTF_TRACK;
        m_ti.hwnd = hWndParent;
        m_ti.hinst = g_hInst;
	
        SendMessageU(m_hwndTT,TTM_ADDTOOL,0,(LPARAM) (LPTOOLINFO) &m_ti);

        SendMessageU(m_hwndTT,TTM_SETMAXTIPWIDTH,0,200);
    }
    else
    {
        MYDBGASSERT(m_hwndTT);
        return FALSE;
    }

    //
    //  Subclass the edit control
    //
    m_pfnOrgBalloonWndProc = (WNDPROC)SetWindowLongU(m_hwndTT, GWLP_WNDPROC, (LONG_PTR)SubClassBalloonWndProc);

    //
    //  Save the this pointer with the window
    //
    SetWindowLongU(m_hwndTT, GWLP_USERDATA, (LONG_PTR)this);

    //
    //	Set the balloon message
    //
    m_ti.lpszText = lpszBalloonMsg;
    SendMessageU(m_hwndTT,TTM_UPDATETIPTEXT,0,(LPARAM) (LPTOOLINFO) &m_ti);

    //
    //  If we got a title, then add it
    //
    if (lpszTitle)
    {
	
        //
        //  confirm we have a valid icon
        //
        if (iIcon > 3)
        {
            iIcon = TTI_NONE;  // TTI_NONE = No icon
        }
		
        SendMessageU(m_hwndTT,TTM_SETTITLE,(WPARAM) iIcon,(LPARAM) lpszTitle);
    }

    //
    //  Set the position
    //
    SendMessageU(m_hwndTT,TTM_TRACKPOSITION,0,(LPARAM) (DWORD) MAKELONG(lppoint->x,lppoint->y));

    //
    //  Show balloon tip window
    //
    SendMessageU(m_hwndTT,TTM_TRACKACTIVATE,(WPARAM) TRUE,(LPARAM) (LPTOOLINFO) &m_ti);

    //  Set active state
    m_bTTActive = TRUE;
	
    return TRUE;

}

//+----------------------------------------------------------------------------
//
// Function:  CBalloonTip::HideBallonTip
//
// Synopsis:  Hides a balloon tip
//
// Arguments: nothing
//
// Returns:   nothing
//
// History:   markcl	Created Header    10/31/00
//
//+----------------------------------------------------------------------------
BOOL CBalloonTip::HideBalloonTip()
{

    // check active state && handle
    if(m_hwndTT && m_bTTActive)
    {
        // hide window
        SendMessageU(m_hwndTT,TTM_TRACKACTIVATE,(WPARAM) FALSE,(LPARAM) (LPTOOLINFO) &m_ti);

        m_bTTActive = FALSE;

        // force a repaint on parent window
        InvalidateRect(m_ti.hwnd,NULL,NULL);

        // destroy window
        DestroyWindow(m_hwndTT);
        m_hwndTT = NULL;

        return TRUE;

    }
    else
    {

        return FALSE;
    
    }

}

//+----------------------------------------------------------------------------
//
// Function:  CBalloonTip::SubClassBalloonWndProc
//
// Synopsis:  Subclassed wnd proc to trap the mouse button clicks on the balloon tip window
//
// Arguments: standard win32 window proc params
//
// Returns:   standard win32 window proc return value
//
// History:   markcl      created         11/2/00
//
//+----------------------------------------------------------------------------
LRESULT CALLBACK CBalloonTip::SubClassBalloonWndProc(HWND hwnd, UINT uMsg, 
                                                      WPARAM wParam, LPARAM lParam)
{

    if ((uMsg == WM_LBUTTONDOWN) || (uMsg == WM_RBUTTONDOWN))
    {
	
        //
        // Get the object pointer saved by SetWindowLong
        //
        CBalloonTip* pBalloonTip = (CBalloonTip*)GetWindowLongU(hwnd, GWLP_USERDATA);
        MYDBGASSERT(pBalloonTip);

        if (pBalloonTip)
        {
            pBalloonTip->HideBalloonTip();
        }
    }

    // 
    // Call the original window procedure for default processing. 
    //
    return CallWindowProcU(m_pfnOrgBalloonWndProc, hwnd, uMsg, wParam, lParam); 
}