#include "shellprv.h"
#include "util.h"
#include "idlcomm.h"
#include <uxtheme.h>
#include <tmschema.h>

// shdocvw and browseui share brutil and menubands need it too
// unfortunately shell32 isnt set up quite like shdocvw and browseui so
// these have to be hacked in a little
#define MAX_BROWSER_WINDOW_TITLE   128
#define g_fRunningOnNT TRUE
#define InitClipboardFormats IDLData_InitializeClipboardFormats
#define g_cfFileDescA g_cfFileGroupDescriptorA
#define g_cfFileDescW g_cfFileGroupDescriptorW
#define g_cfURL g_cfShellURL
#define PVAREMPTY ((VARIANT*)&c_vaEmpty)
#define NO_MLUI_IN_SHELL32
#include "..\inc\brutil.cpp"
const VARIANT c_vaEmpty = {0};


HRESULT QueryService_SID_IBandProxy(IUnknown * punkParent, REFIID riid, IBandProxy ** ppbp, void **ppvObj)
{
    HRESULT hr = E_FAIL;

    if (ppbp)
    {
        if (NULL == (*ppbp))
            hr = IUnknown_QueryService(punkParent, SID_IBandProxy, IID_PPV_ARG(IBandProxy, ppbp));

        if (*ppbp && ppvObj)
            hr = (*ppbp)->QueryInterface(riid, ppvObj);        // They already have the object.
    }


    return hr;
}

HRESULT CreateIBandProxyAndSetSite(IUnknown * punkParent, REFIID riid, IBandProxy ** ppbp, void **ppvObj)
{
    ASSERT(ppbp);

    HRESULT hr = CoCreateInstance(CLSID_BandProxy, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBandProxy, ppbp));
    if (SUCCEEDED(hr))
    {
        // Set the site
        ASSERT(*ppbp);
        (*ppbp)->SetSite(punkParent);

        if (ppvObj)
            hr = (*ppbp)->QueryInterface(riid, ppvObj);
    }
    else
    {
        if (ppvObj)
            *ppvObj = NULL;
    }
    return hr;
}

typedef struct tagINIPAIR
{
    DWORD dwFlags;
    LPCTSTR pszSection;
}
INIPAIR;

const INIPAIR c_aIniPairs[] =
{
    EICH_KINET,          TEXT("Software\\Microsoft\\Internet Explorer"),
    EICH_KINETMAIN,      TEXT("Software\\Microsoft\\Internet Explorer\\Main"),
    EICH_KWIN,           TEXT("Software\\Microsoft\\Windows\\CurrentVersion"),
    EICH_KWINEXPLORER,   TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"),
    EICH_KWINEXPLSMICO,  TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SmallIcons"),
    EICH_KWINPOLICY,     TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies"),
    EICH_SSAVETASKBAR,   TEXT("SaveTaskbar"),
    EICH_SWINDOWMETRICS, TEXT("WindowMetrics"),
    EICH_SSHELLMENU,     TEXT("ShellMenu"),
    EICH_SPOLICY,        TEXT("Policy"),
    EICH_SWINDOWS,       TEXT("Windows"),
};

DWORD SHIsExplorerIniChange(WPARAM wParam, LPARAM lParam)
{
    DWORD dwFlags = 0;

    if (lParam == 0)
    {
        if (wParam == 0)
        {
            dwFlags = EICH_UNKNOWN;
        }
    }
    else
    {
        //
        // In the wacky world of browseui, UNICODE-ANSI doesn't vary from
        // window to window.  Instead, on NT browseui registers all windows
        // UNICODE, while on 9x user browseui registers all windows ANSI.
        //
        USES_CONVERSION;
        LPCTSTR pszSection = W2CT((LPCWSTR)lParam);

        for (int i = 0; !dwFlags && i < ARRAYSIZE(c_aIniPairs); i++)
        {
            if (StrCmpI(pszSection, c_aIniPairs[i].pszSection) == 0)
            {
                dwFlags = c_aIniPairs[i].dwFlags;
            }
        }
    }

    return dwFlags;
}

ULONG RegisterNotify(HWND hwnd, UINT nMsg, LPCITEMIDLIST pidl, DWORD dwEvents, UINT uFlags, BOOL fRecursive)
{
    SHChangeNotifyEntry fsne;

    uFlags |= SHCNRF_NewDelivery;

    fsne.fRecursive = fRecursive;
    fsne.pidl = pidl;
    return SHChangeNotifyRegister(hwnd, uFlags, dwEvents, nMsg, 1, &fsne);
}

BOOL GetInfoTipEx(IShellFolder* psf, DWORD dwFlags, LPCITEMIDLIST pidl, LPTSTR pszText, int cchTextMax)
{
    BOOL fRet = FALSE;

    *pszText = 0;   // empty for failure

    if (pidl)
    {
        IQueryInfo *pqi;
        if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidl, IID_X_PPV_ARG(IQueryInfo, NULL, &pqi))))
        {
            WCHAR *pwszTip;
            pqi->GetInfoTip(dwFlags, &pwszTip);
            if (pwszTip)
            {
                fRet = TRUE;
                SHUnicodeToTChar(pwszTip, pszText, cchTextMax);
                SHFree(pwszTip);
            }
            pqi->Release();
        }
    }
    return fRet;
}

BOOL GetInfoTip(IShellFolder* psf, LPCITEMIDLIST pidl, LPTSTR pszText, int cchTextMax)
{
    return GetInfoTipEx(psf, 0, pidl, pszText, cchTextMax);
}

BOOL IsBrowsableShellExt(LPCITEMIDLIST pidl)
{
    DWORD    cb;
    LPCTSTR pszExt;
    TCHAR   szFile[MAX_PATH];
    TCHAR   szProgID[80];   // From ..\shell32\fstreex.c
    TCHAR   szCLSID[GUIDSTR_MAX], szCATID[GUIDSTR_MAX];
    TCHAR   szKey[GUIDSTR_MAX * 4];
    HKEY    hkeyProgID = NULL;
    BOOL    fRet = FALSE;

    for (;;)
    {
        // Make sure we have a file extension
        if  (
            !SHGetPathFromIDList(pidl, szFile)
            ||
            ((pszExt = PathFindExtension(szFile)) == NULL)
            ||
            (pszExt[0] != TEXT('.'))
           )
        {
            break;
        }

        // Get the ProgID.
        cb = sizeof(szProgID);
        if  (
            (SHGetValue(HKEY_CLASSES_ROOT, pszExt, NULL, NULL, szProgID, &cb) != ERROR_SUCCESS)
            ||
            (RegOpenKey(HKEY_CLASSES_ROOT, szProgID, &hkeyProgID) != ERROR_SUCCESS)
           )
        {
            break;
        }

        // From the ProgID, get the CLSID.
        cb = sizeof(szCLSID);
        if (SHGetValue(hkeyProgID, TEXT("CLSID"), NULL, NULL, szCLSID, &cb) != ERROR_SUCCESS)
            break;

        // Construct the registry key that detects if
        // a CLSID is a member of a CATID.
        SHStringFromGUID(CATID_BrowsableShellExt, szCATID, ARRAYSIZE(szCATID));
        wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("CLSID\\%s\\Implemented Categories\\%s"),
                 szCLSID, szCATID);

        // See if it's there.
        cb = 0;
        if (SHGetValue(HKEY_CLASSES_ROOT, szKey, NULL, NULL, NULL, &cb) != ERROR_SUCCESS)
            break;

        fRet = TRUE;
        break;
    }

    if (hkeyProgID != NULL)
        RegCloseKey(hkeyProgID);

    return fRet;
}


void OpenFolderPidl(LPCITEMIDLIST pidl)
{
    SHELLEXECUTEINFO shei = { 0 };

    shei.cbSize     = sizeof(shei);
    shei.fMask      = SEE_MASK_INVOKEIDLIST;
    shei.nShow      = SW_SHOWNORMAL;
    shei.lpIDList   = (LPITEMIDLIST)pidl;
    ShellExecuteEx(&shei);
}

// As perf, share IShellLink implementations between bands and ask
// the bandsite for an implementation. Don't rely on the bandsite
// because you never know who will host us in the future. (And bandsite
// can change to not have us hosted at save/load time. Ex: it doesn't
// set our site before loading us from the stream, which sounds buggy.)
//
HRESULT SavePidlAsLink(IUnknown* punkSite, IStream *pstm, LPCITEMIDLIST pidl)
{
    HRESULT hr = E_FAIL;
    IShellLinkA* psl;

    if (punkSite)
        hr = IUnknown_QueryService(punkSite, IID_IBandSite, IID_PPV_ARG(IShellLinkA, &psl));
    if (FAILED(hr))
        hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLinkA, &psl));
    if (SUCCEEDED(hr))
    {
        IPersistStream *pps;
        hr = psl->QueryInterface(IID_PPV_ARG(IPersistStream, &pps));
        if (EVAL(SUCCEEDED(hr)))
        {
            ASSERT(pidl);
            psl->SetIDList(pidl);

            hr = pps->Save(pstm, FALSE);
            pps->Release();
        }
        psl->Release();
    }
    return hr;
}

HRESULT LoadPidlAsLink(IUnknown* punkSite, IStream *pstm, LPITEMIDLIST *ppidl)
{
    IShellLinkA* psl;
    HRESULT hr = IUnknown_QueryService(punkSite, IID_IBandSite, IID_PPV_ARG(IShellLinkA, &psl));
    if (FAILED(hr))
        hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLinkA, &psl));
    if (SUCCEEDED(hr))
    {
        IPersistStream *pps;
        hr = psl->QueryInterface(IID_PPV_ARG(IPersistStream, &pps));
        if (EVAL(SUCCEEDED(hr)))
        {
            hr = pps->Load(pstm);
            if (EVAL(SUCCEEDED(hr)))
            {
                hr = psl->GetIDList(ppidl);

                // Don't make me resolve the link because it's soo slow because
                // it often loads 80k of networking dlls.
                if (!EVAL(SUCCEEDED(hr)))
                {
                    hr = psl->Resolve(NULL, SLR_NOUPDATE | SLR_NO_UI);
                    if (EVAL(SUCCEEDED(hr)))
                        hr = psl->GetIDList(ppidl);
                }

                hr = *ppidl ? S_OK : E_FAIL;
            }
            pps->Release();
        }
        psl->Release();
    }
    return hr;
}


// Review chrisny:  this can be moved into an object easily to handle generic droptarget, dropcursor
// , autoscrool, etc. . .
void _DragEnter(HWND hwndTarget, const POINTL ptStart, IDataObject *pdtObject)
{
    RECT    rc;
    POINT   pt;

    GetWindowRect(hwndTarget, &rc);

    //
    // If hwndTarget is RTL mirrored, then measure the
    // the client point from the visual right edge
    // (near edge in RTL mirrored windows). [samera]
    //
    if (IS_WINDOW_RTL_MIRRORED(hwndTarget))
        pt.x = rc.right - ptStart.x;
    else
        pt.x = ptStart.x - rc.left;

    pt.y = ptStart.y - rc.top;
    DAD_DragEnterEx2(hwndTarget, pt, pdtObject);
    return;
}

void _DragMove(HWND hwndTarget, const POINTL ptStart)
{
    RECT rc;
    POINT pt;

    GetWindowRect(hwndTarget, &rc);

    //
    // If hwndTarget is RTL mirrored, then measure the
    // the client point from the visual right edge
    // (near edge in RTL mirrored windows). [samera]
    //
    if (IS_WINDOW_RTL_MIRRORED(hwndTarget))
        pt.x = rc.right - ptStart.x;
    else
        pt.x = ptStart.x - rc.left;

    pt.y = ptStart.y - rc.top;
    DAD_DragMove(pt);
    return;
}


// Can we browse or navigate to this pidl?  If not, need
BOOL ILIsBrowsable(LPCITEMIDLIST pidl, BOOL *pfIsFolder)
{
    if (!pidl)
        return FALSE;
    DWORD dwAttributes = SFGAO_FOLDER | SFGAO_BROWSABLE;
    HRESULT hr = IEGetAttributesOf(pidl, &dwAttributes);

    if (pfIsFolder && SUCCEEDED(hr))
        *pfIsFolder = dwAttributes & SFGAO_FOLDER;

    return SUCCEEDED(hr) && (dwAttributes & (SFGAO_FOLDER | SFGAO_BROWSABLE));
}

// gets a target pidl given a name space item. typically this is a .lnk or .url file
//
//  in:
//        psf         shell folder for item
//        pidl        item relative to psf, single level
//
//  in/out
//        pdwAttribs  [optional] attributes mask to filter on (returned).
//        must be initalized
//
//
//  returns
//        *ppidl      the target pidl
//        *pdwAttribs [optional] attributes of the source object

STDAPI SHGetNavigateTarget(IShellFolder *psf, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl, DWORD *pdwAttribs)
{
    ASSERT(IS_VALID_CODE_PTR(psf, IShellFolder));
    ASSERT(IS_VALID_PIDL(pidl));
    ASSERT(NULL == pdwAttribs || IS_VALID_WRITE_PTR(pdwAttribs, DWORD));
    ASSERT(ILFindLastID(pidl) == pidl);   // must be single level PIDL

    *ppidl = NULL;      // assume failure

    DWORD dwAttribs = SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_LINK | SFGAO_BROWSABLE;

    if (pdwAttribs)
        dwAttribs |= *pdwAttribs;

    HRESULT hr = psf->GetAttributesOf(1, &pidl, &dwAttribs);
    if (SUCCEEDED(hr))
    {
        // first try the most efficient way
        IShellLinkA *psl;       // "A" so this works on Win95
        hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_X_PPV_ARG(IShellLinkA, NULL, &psl));
        if (SUCCEEDED(hr))
        {
            hr = psl->GetIDList(ppidl);
            psl->Release();
        }

        // this is for .lnk and .url files that don't register properly
        if (FAILED(hr) && (dwAttribs & (SFGAO_FILESYSTEM | SFGAO_LINK)) == (SFGAO_FILESYSTEM | SFGAO_LINK))
        {
            TCHAR szPath[MAX_PATH];

            hr = GetPathForItem(psf, pidl, szPath, NULL);
            if (SUCCEEDED(hr))
                hr = GetLinkTargetIDList(szPath, NULL, 0, ppidl);
        }

        // .doc or .html. return the pidl for this.
        // (fully qualify against the folder pidl)
        if (FAILED(hr) && (dwAttribs & SFGAO_BROWSABLE))
        {
            LPITEMIDLIST pidlFolder;
            hr = SHGetIDListFromUnk(psf, &pidlFolder);
            if (SUCCEEDED(hr))
            {
                *ppidl = ILCombine(pidlFolder, pidl); // navigate to this thing...
                hr = *ppidl ? S_OK : E_OUTOFMEMORY;
                ILFree(pidlFolder);
            }
        }

        // Callers of SHGetNavigateTarget assume that the returned pidl
        // is navigatable (SFGAO_FOLDER or SFGAO_BROWSER), which isn't
        // the case for a link (it could be a link to an exe).
        //
        if (SUCCEEDED(hr) && !ILIsBrowsable(*ppidl, NULL))
        {
            ILFree(*ppidl);
            *ppidl = NULL;
            hr = E_FAIL;
        }

        if (SUCCEEDED(hr) && pdwAttribs)
            *pdwAttribs = dwAttribs;
    }
    return hr;
}

STDAPI SHNavigateToFavorite(IShellFolder* psf, LPCITEMIDLIST pidl, IUnknown* punkSite, DWORD dwFlags)
{
    HRESULT hr = S_FALSE;

    TCHAR szPath[MAX_PATH];

    // Can we navigate to this favorite?
    BOOL fNavigateDone = SUCCEEDED(GetPathForItem(psf, pidl, szPath, NULL)) &&
                         SUCCEEDED(NavFrameWithFile(szPath, punkSite));
    if (fNavigateDone)
        return S_OK;

    LPITEMIDLIST pidlGoto;

    ASSERT(!(dwFlags & (SBSP_NEWBROWSER | SBSP_SAMEBROWSER)));
    
    if (SUCCEEDED(SHGetNavigateTarget(psf, pidl, &pidlGoto, NULL)))
    {
        IShellBrowser* psb;
        if (SUCCEEDED(IUnknown_QueryService(punkSite, SID_STopLevelBrowser,
            IID_PPV_ARG(IShellBrowser, &psb))))
        {
            hr = psb->BrowseObject(pidlGoto, dwFlags | SBSP_SAMEBROWSER);
            psb->Release();
        }
        ILFree(pidlGoto);
    }
    return hr;
}

STDAPI SHGetTopBrowserWindow(IUnknown* punk, HWND* phwnd)
{
    IOleWindow* pOleWindow;
    HRESULT hr = IUnknown_QueryService(punk, SID_STopLevelBrowser, IID_PPV_ARG(IOleWindow, &pOleWindow));
    if (SUCCEEDED(hr))
    {
        hr = pOleWindow->GetWindow(phwnd);
        pOleWindow->Release();
    }
    return hr;
}

void SHOutlineRect(HDC hdc, const RECT* prc, COLORREF cr)
{
    RECT rc;
    COLORREF clrSave = SetBkColor(hdc, cr);
    
    //top
    rc.left = prc->left;
    rc.top = prc->top;
    rc.right = prc->right;
    rc.bottom = prc->top + 1;
    ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);

    //left
    rc.left = prc->left;
    rc.top = prc->top;
    rc.right = prc->left + 1;
    rc.bottom = prc->bottom;
    ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);

    //right
    rc.left = prc->right - 1;
    rc.top = prc->top;
    rc.right = prc->right;
    rc.bottom = prc->bottom;
    ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);

    // bottom
    rc.left = prc->left;
    rc.top = prc->bottom - 1;
    rc.right = prc->right;
    rc.bottom = prc->bottom;
    ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);

    SetBkColor(hdc, clrSave);
}

STDAPI NavigateToPIDL(IWebBrowser2* pwb, LPCITEMIDLIST pidl)
{
    ASSERT(pwb);
    ASSERT(pidl);

    VARIANT varThePidl;
    HRESULT hr = InitVariantFromIDList(&varThePidl, pidl);
    if (SUCCEEDED(hr))
    {
        hr = pwb->Navigate2(&varThePidl, PVAREMPTY, PVAREMPTY, PVAREMPTY, PVAREMPTY);
        VariantClear(&varThePidl);       // Needed to free the copy of the PIDL in varThePidl.
    }
    return hr;
}

HRESULT Channels_OpenBrowser(IWebBrowser2 **ppwb, BOOL fInPlace)
{
    HRESULT hr;
    IWebBrowser2* pwb;

    if (fInPlace) 
    {
        ASSERT(ppwb && *ppwb != NULL);
        pwb = *ppwb;
        hr = S_OK;
    }
    else 
    {
        hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(IWebBrowser2, &pwb));
    }
    
    if (SUCCEEDED(hr))
    {
        // Don't special case full-screen mode for channels post IE4.  Use the
        // browser's full screen setting.
        // 
        //BOOL fTheater = SHRegGetBoolUSValue(TEXT("Software\\Microsoft\\Internet Explorer\\Channels"),
        BOOL fTheater = SHRegGetBoolUSValue(TEXT("Software\\Microsoft\\Internet Explorer\\Main"),
                                            TEXT("FullScreen"), FALSE, FALSE);
        pwb->put_TheaterMode( fTheater ? VARIANT_TRUE : VARIANT_FALSE);
        pwb->put_Visible(VARIANT_TRUE);


        if (!SHRestricted2(REST_NoChannelUI, NULL, 0))
        {
            SA_BSTRGUID  strGuid;
            VARIANT      vaGuid;

            InitFakeBSTR(&strGuid, CLSID_FavBand);

            vaGuid.vt = VT_BSTR;
            vaGuid.bstrVal = strGuid.wsz;

            pwb->ShowBrowserBar(&vaGuid, PVAREMPTY, PVAREMPTY);
        }
        
        // don't release, we're going to return pwb.
    }
    
    if (ppwb)
        *ppwb = pwb;
    else if (pwb)
        pwb->Release();
    
    return hr;
}

//
//  Just like TB_GETBUTTONSIZE except that theme borders are subtracted out.
//
LRESULT TB_GetButtonSizeWithoutThemeBorder(HWND hwndTB, HTHEME hThemeParent)
{
    LRESULT lButtonSize = SendMessage(hwndTB, TB_GETBUTTONSIZE, 0, 0L);
    if (hThemeParent)
    {
        HTHEME hTheme = OpenThemeData(hwndTB, L"Toolbar");
        if (hTheme)
        {
            RECT rc = { 0, 0, 0, 0 };
            if (SUCCEEDED(GetThemeBackgroundExtent(hTheme, NULL, TP_BUTTON, TS_NORMAL, &rc, &rc)))
            {
                lButtonSize = MAKELONG(LOWORD(lButtonSize) - RECTWIDTH(rc),
                                       HIWORD(lButtonSize) - RECTHEIGHT(rc));
            }
            CloseThemeData(hTheme);
        }
    }
    return lButtonSize;
}

#ifdef DEBUG

extern "C" void DumpMsg(LPCTSTR pszLabel, MSG * pmsg)
{
    ASSERT(IS_VALID_STRING_PTR(pszLabel, -1));
    ASSERT(pmsg);

    switch (pmsg->message)
    {
    case WM_LBUTTONDOWN:
        TraceMsg(TF_ALWAYS, "%s: msg = WM_LBUTTONDOWN hwnd = %#08lx  x = %d  y = %d",
                 pszLabel, pmsg->hwnd, pmsg->pt.x, pmsg->pt.y);
        TraceMsg(TF_ALWAYS, "                              keys = %#04lx  x = %d  y = %d",
                 pmsg->wParam, LOWORD(pmsg->lParam), HIWORD(pmsg->lParam));
        break;

    case WM_LBUTTONUP:
        TraceMsg(TF_ALWAYS, "%s: msg = WM_LBUTTONUP   hwnd = %#08lx  x = %d  y = %d",
                 pszLabel, pmsg->hwnd, pmsg->pt.x, pmsg->pt.y);
        TraceMsg(TF_ALWAYS, "                              keys = %#04lx  x = %d  y = %d",
                 pmsg->wParam, LOWORD(pmsg->lParam), HIWORD(pmsg->lParam));
        break;

    case WM_KEYDOWN:
    case WM_SYSKEYDOWN:
    case WM_KEYUP:
    case WM_SYSKEYUP:
        BLOCK
        {
            LPTSTR pcsz = TEXT("(unknown)");
            switch (pmsg->message)
            {
                STRING_CASE(WM_KEYDOWN);
                STRING_CASE(WM_SYSKEYDOWN);
                STRING_CASE(WM_KEYUP);
                STRING_CASE(WM_SYSKEYUP);
            }

            TraceMsg(TF_ALWAYS, "%s: msg = %s     hwnd = %#08lx",
                     pszLabel, pcsz, pmsg->hwnd);
            TraceMsg(TF_ALWAYS, "            vk = %#04lx  count = %u  flags = %#04lx",
                     pmsg->wParam, LOWORD(pmsg->lParam), HIWORD(pmsg->lParam));
        }
        break;

    case WM_CHAR:
    case WM_SYSCHAR:
        BLOCK
        {
            LPTSTR pcsz = TEXT("(unknown)");
            switch (pmsg->message)
            {
                STRING_CASE(WM_CHAR);
                STRING_CASE(WM_SYSCHAR);
            }

            TraceMsg(TF_ALWAYS, "%s: msg = %s     hwnd = %#08lx",
                     pszLabel, pcsz, pmsg->hwnd);
            TraceMsg(TF_ALWAYS, "            char = '%c'  count = %u  flags = %#04lx",
                     pmsg->wParam, LOWORD(pmsg->lParam), HIWORD(pmsg->lParam));
        }
        break;

    case WM_MOUSEMOVE:
#if 0
        TraceMsg(TF_ALWAYS, "%s: msg = WM_MOUSEMOVE hwnd = %#08lx  x=%d  y=%d",
                 pszLabel, pmsg->hwnd, LOWORD(pmsg->lParam), HIWORD(pmsg->lParam));
#endif
        break;

    case WM_TIMER:
#if 0
        TraceMsg(TF_ALWAYS, "%s: msg = WM_TIMER       hwnd = %#08lx  x = %d  y = %d",
                 pszLabel, pmsg->hwnd, pmsg->pt.x, pmsg->pt.y);
        TraceMsg(TF_ALWAYS, "                              id = %#08lx",
                 pmsg->wParam);
#endif
        break;

    case WM_MENUSELECT:
        TraceMsg(TF_ALWAYS, "%s: msg = WM_MENUSELECT  hwnd = %#08lx  x = %d  y = %d",
                 pszLabel, pmsg->hwnd, pmsg->pt.x, pmsg->pt.y);
        TraceMsg(TF_ALWAYS, "                              uItem = %#04lx  flags = %#04lx  hmenu = %#08lx",
                 GET_WM_MENUSELECT_CMD(pmsg->wParam, pmsg->lParam),
                 GET_WM_MENUSELECT_FLAGS(pmsg->wParam, pmsg->lParam),
                 GET_WM_MENUSELECT_HMENU(pmsg->wParam, pmsg->lParam));
        break;

    default:
        if (WM_USER > pmsg->message)
        {
            TraceMsg(TF_ALWAYS, "%s: msg = %#04lx    hwnd=%#04lx wP=%#08lx lP=%#08lx",
                     pszLabel, pmsg->message, pmsg->hwnd, pmsg->wParam, pmsg->lParam);
        }
        break;
    }
}    
#endif