//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1997.
//
//  File:       S H U T I L . C P P
//
//  Contents:   Various shell utilities to be used by the connections shell
//
//  Notes:
//
//  Author:     jeffspr   21 Oct 1997
//
//----------------------------------------------------------------------------

#include "pch.h"
#pragma hdrstop

//+---------------------------------------------------------------------------
//
//  Function:   HrDupeShellStringLength
//
//  Purpose:    Duplicate a string using SHAlloc, so we can return it to the
//              shell. This is required because the shell typically releases
//              the strings that we pass it (so we need to use their
//              allocator).
//
//  Arguments:
//      pszInput   [in]  String to duplicate
//      cchInput   [in]  Count of characters to copy (not including null term)
//      ppszOutput [out] Return pointer for the newly allocated string.
//
//  Returns:
//
//  Author:     jeffspr   21 Oct 1997
//
//  Notes:
//
HRESULT HrDupeShellStringLength(
    PCWSTR     pszInput,
    ULONG      cchInput,
    PWSTR *    ppszOutput)
{
    HRESULT hr = S_OK;

    Assert(pszInput);
    Assert(ppszOutput);

    ULONG cbString = (cchInput + 1) * sizeof(WCHAR);

    // Allocate a new POLESTR block, which the shell can then free.
    //
    PWSTR pszOutput = (PWSTR) SHAlloc(cbString);

    // If the alloc failed, return E_OUTOFMEMORY
    //
    if (NULL != pszOutput)
    {
        // Copy the memory into the alloc'd block
        //
        CopyMemory(pszOutput, pszInput, cbString);
        pszOutput[cchInput] = 0;
        *ppszOutput = pszOutput;
    }
    else
    {
        *ppszOutput = NULL;
        hr = E_OUTOFMEMORY;
    }

    TraceHr(ttidError, FAL, hr, FALSE, "HrDupeShellStringLength");
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Function:   HrLoadPopupMenu
//
//  Purpose:    Load a popup menu as the first child of a loadable parent
//              menu
//
//  Arguments:
//      hinst  [in]     Our instance handle
//      id     [in]     ID of the parent menu
//      phmenu [out]    Return pointer for the popup menu
//
//  Returns:
//
//  Author:     jeffspr   27 Oct 1997
//
//  Notes:
//
HRESULT HrLoadPopupMenu(
    HINSTANCE   hinst,
    UINT        id,
    HMENU *     phmenu)
{
    HRESULT hr          = S_OK;
    HMENU   hmParent    = NULL;
    HMENU   hmPopup     = NULL;

    Assert(id);
    Assert(hinst);
    Assert(phmenu);

    // Load the parent menu
    //
    hmParent = LoadMenu(hinst, MAKEINTRESOURCE(id));
    if (NULL == hmParent)
    {
        AssertSz(FALSE, "Can't load parent menu in HrLoadPopupMenu");
        hr = HrFromLastWin32Error();
    }
    else
    {
        // Load the popup from the parent (first submenu), then
        // remove the parent menu
        //
        hmPopup = GetSubMenu(hmParent, 0);
        RemoveMenu(hmParent, 0, MF_BYPOSITION);
        DestroyMenu(hmParent);
    }

    if (phmenu)
    {
        *phmenu = hmPopup;
    }

    TraceHr(ttidError, FAL, hr, FALSE, "HrLoadPopupMenu");
    return hr;
}


HRESULT HrGetMenuFromID(
    HMENU   hmenuMain,
    UINT    uID,
    HMENU * phmenu)
{
    HRESULT         hr          = S_OK;
    HMENU           hmenuReturn = NULL;
    MENUITEMINFO    mii = {0};

    Assert(hmenuMain);
    Assert(uID);
    Assert(phmenu);

    mii.cbSize = sizeof(mii);
    mii.fMask  = MIIM_SUBMENU;
    mii.cch    = 0;     // just in case

    if (!GetMenuItemInfo(hmenuMain, uID, FALSE, &mii))
    {
        hr = E_FAIL;
    }
    else
    {
        hmenuReturn = mii.hSubMenu;
    }

    if (phmenu)
    {
        *phmenu = mii.hSubMenu;
    }

    TraceHr(ttidError, FAL, hr, FALSE, "HrGetMenuFromID");
    return hr;
}


INT IMergePopupMenus(
    HMENU hmMain,
    HMENU hmMerge,
    int   idCmdFirst,
    int   idCmdLast)
{
    HRESULT hr      = S_OK;
    int     iCount  = 0;
    int     idTemp  = 0;
    int     idMax   = idCmdFirst;
    HMENU   hmFromId = NULL;

    for (iCount = GetMenuItemCount(hmMerge) - 1; iCount >= 0; --iCount)
    {
        MENUITEMINFO mii = {0};

        mii.cbSize = sizeof(mii);
        mii.fMask  = MIIM_ID | MIIM_SUBMENU;
        mii.cch    = 0;     // just in case
        mii.hSubMenu = NULL;

        if (!GetMenuItemInfo(hmMerge, iCount, TRUE, &mii))
        {
            TraceHr(ttidError, FAL, E_FAIL, FALSE, "GetMenuItemInfo failed in iMergePopupMenus");
            continue;
        }

        hr = HrGetMenuFromID(hmMain, mii.wID, &hmFromId);
        if (SUCCEEDED(hr))
        {
            idTemp = Shell_MergeMenus(
                        hmFromId,
                        mii.hSubMenu,
                        0,
                        idCmdFirst,
                        idCmdLast,
                        MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);

            if (idMax < idTemp)
            {
                idMax = idTemp;
            }
        }
        else
        {
            TraceHr(ttidError, FAL, E_FAIL, FALSE, "HrGetMenuFromId failed in iMergePopupMenus");
            continue;
        }
    }

    return idMax;
}


VOID MergeMenu(
    HINSTANCE   hinst,
    UINT        idMainMerge,
    UINT        idPopupMerge,
    LPQCMINFO   pqcm)
{
    HMENU hmMerge   = NULL;
    UINT  idMax     = 0;
    UINT  idTemp    = 0;

    Assert(pqcm);
    Assert(idMainMerge || idPopupMerge);
    Assert(hinst);

    idMax = pqcm->idCmdFirst;

    if (idMainMerge
        && (SUCCEEDED(HrLoadPopupMenu(hinst, idMainMerge, &hmMerge))))
    {
        Assert(hmMerge);

        if (hmMerge)
        {
            idMax = Shell_MergeMenus(
                            pqcm->hmenu,
                            hmMerge,
                            pqcm->indexMenu,
                            pqcm->idCmdFirst,
                            pqcm->idCmdLast,
                            MM_SUBMENUSHAVEIDS);

            DestroyMenu(hmMerge);
        }
    }

    if (idPopupMerge
        && (hmMerge = LoadMenu(hinst, MAKEINTRESOURCE(idPopupMerge))) != NULL)
    {
        idTemp = IMergePopupMenus(
                        pqcm->hmenu,
                        hmMerge,
                        pqcm->idCmdFirst,
                        pqcm->idCmdLast);

        if (idMax < idTemp)
        {
            idMax = idTemp;
        }

        DestroyMenu(hmMerge);
    }

    pqcm->idCmdFirst = idMax;
}

//+---------------------------------------------------------------------------
//
//  Function:   GenerateEvent
//
//  Purpose:    Generate a Shell Notification event.
//
//  Arguments:
//      lEventId   [in]     The event ID to post
//      pidlFolder [in]     Folder pidl
//      pidlIn     [in]     First pidl that we reference
//      pidlNewIn  [in]     If needed, the second pidl.
//
//  Returns:
//
//  Author:     jeffspr   16 Dec 1997
//
//  Notes:
//
VOID GenerateEvent(LONG lEventId, const LPCITEMIDLIST pidlFolder,
                   LPCITEMIDLIST  pidlIn, LPCITEMIDLIST pidlNewIn)
{
    // Build an absolute pidl from the folder pidl + the object pidl
    //
    LPITEMIDLIST pidl = ILCombinePriv(pidlFolder, pidlIn);
    if (pidl)
    {
        // If we have two pidls, call the notify with both
        //
        if (pidlNewIn)
        {
            // Build the second absolute pidl
            //

            LPITEMIDLIST pidlNew = ILCombinePriv(pidlFolder, pidlNewIn);
            if (pidlNew)
            {
                // Make the notification, and free the new pidl
                //
                SHChangeNotify(lEventId, SHCNF_IDLIST, pidl, pidlNew);
                FreeIDL(pidlNew);
            }
        }
        else
        {
            // Make the single-pidl notification
            //
            SHChangeNotify(lEventId, SHCNF_IDLIST, pidl, NULL);
        }

        // Always refresh, then free the newly allocated pidl
        //
        SHChangeNotifyHandleEvents();
        FreeIDL(pidl);
    }
}

VOID ForceRefresh(HWND hwnd)
{
    LPSHELLBROWSER  psb = FileCabinet_GetIShellBrowser(hwnd);
    LPSHELLVIEW     psv = NULL;

    // Did we get the shellview?
#if 0   // We can't require this, since we may need to refresh without a folder
        // actually being open
    AssertSz(psb, "FileCabinet_GetIShellBrowser failed in ForceRefresh()");
#endif

    if (psb && SUCCEEDED(psb->QueryActiveShellView(&psv)))
    {
        // $$TODO: Flush the connection list
        //

        Assert(psv);
        if (psv)
        {
            psv->Refresh();
            psv->Release();
        }
    }
    else
    {
        // $$TODO: Refresh the connection list.
    }
}


//+---------------------------------------------------------------------------
//
//  Function:   HrShellView_GetSelectedObjects
//
//  Purpose:    Get the selected data objects. We only care about the first
//              one (we'll ignore the rest)
//
//  Arguments:
//      hwnd            [in]    Our window handle
//      papidlSelection [out]   Return array for selected pidls
//      lpcidl          [out]   Count of returned pidls
//
//  Returns:    S_OK if 1 or more items are selected.
//              S_FALSE if 0 items are selected
//              OLE HRESULT otherwise
//
//  Author:     jeffspr   13 Jan 1998
//
//  Notes:
//
HRESULT HrShellView_GetSelectedObjects(
    HWND                hwnd,
    LPCITEMIDLIST **    papidlSelection,
    UINT *              lpcidl)
{
    HRESULT         hr      = S_OK;
    LPCITEMIDLIST * apidl   = NULL;
    UINT            cpidl   = 0;

    // Get the selected object list from the shell
    //
    cpidl = ShellFolderView_GetSelectedObjects(hwnd, &apidl);

    // If the GetSelectedObjects failed, NULL out the return
    // params.
    //
    if (-1 == cpidl)
    {
        cpidl = 0;
        apidl = NULL;
        hr = E_OUTOFMEMORY;
    }
    else
    {
        // If no items were selected, return S_FALSE
        //
        if (0 == cpidl)
        {
            Assert(!apidl);
            hr = S_FALSE;
        }
    }

    // Fill in the out params
    //
    *papidlSelection = apidl;
    *lpcidl = cpidl;

    TraceHr(ttidError, FAL, hr, (S_FALSE == hr),
        "HrShellView_GetSelectedObjects");
    return hr;
}