/*--------------------------------------------------------------------------*
 *
 *  Microsoft Windows
 *  Copyright (C) Microsoft Corporation, 1992 - 1999
 *
 *  File:      sysmenu.cpp
 *
 *  Contents:  Implementation file for system menu modification functions
 *
 *  History:   04-Feb-98 jeffro     Created
 *
 *--------------------------------------------------------------------------*/

#include "stdafx.h"
#include "sysmenu.h"
#include "mmcres.h"
#include <list>
#include <algorithm>


typedef std::list<HWND> WindowList;



/*--------------------------------------------------------------------------*
 * GetWindowList
 *
 *
 *--------------------------------------------------------------------------*/

static WindowList& GetWindowList()
{
    static WindowList   List;
    return (List);
}



/*--------------------------------------------------------------------------*
 * WipeWindowList
 *
 * Removes no-longer-valid windows from the Window-to-addition map.
 *--------------------------------------------------------------------------*/

void WipeWindowList ()
{
    WindowList&             List = GetWindowList();
    WindowList::iterator    it   = List.begin();

    while (it != List.end())
    {
        // if the window isn't valid, erase it
        if (!::IsWindow (*it))
        {
            WindowList::iterator itErase = it++;
            List.erase(itErase);
        }

        // this one's OK, check the next one
        else
            ++it;
    }
}



/*--------------------------------------------------------------------------*
 * AppendToSystemMenu
 *
 * Returns number of menu items appended.
 *--------------------------------------------------------------------------*/

int AppendToSystemMenu (CWnd* pwnd, int nSubmenuIndex)
{
    DECLARE_SC(sc, TEXT("AppendToSystemMenu"));

    CMenu   menuSysAdditions;
    sc = menuSysAdditions.LoadMenu (IDR_SYSMENU_ADDITIONS) ? S_OK : E_FAIL;
    if (sc)
        return 0;

    CMenu*  pSubMenu = menuSysAdditions.GetSubMenu (nSubmenuIndex);
    sc = ScCheckPointers(pSubMenu, E_UNEXPECTED);
    if (sc)
        return 0;

    return (AppendToSystemMenu (pwnd, pSubMenu));
}



/*--------------------------------------------------------------------------*
 * AppendToSystemMenu
 *
 * Returns number of menu items appended.
 *
 *--------------------------------------------------------------------------*/

int AppendToSystemMenu (CWnd* pwnd, CMenu* pMenuToAppend, CMenu* pSysMenu)
{
    DECLARE_SC(sc, TEXT("AppendToSystemMenu"));
    sc = ScCheckPointers(pwnd, pMenuToAppend);
    if (sc)
        return 0;

    if ( (!::IsWindow (pwnd->m_hWnd)) ||
         (!::IsMenu (pMenuToAppend->m_hMenu)) )
    {
        sc = E_UNEXPECTED;
        return 0;
    }

    // no system menu?  get one
    if (pSysMenu == NULL)
        pSysMenu = pwnd->GetSystemMenu (FALSE);

    // still no system menu?  bail
    if (pSysMenu == NULL)
        return (0);

    // clean out the map
    WipeWindowList ();

    // if this is the first addition to this window, append a separator
    WindowList& List = GetWindowList();
    WindowList::iterator itEnd = List.end();

    if (std::find (List.begin(), itEnd, pwnd->m_hWnd) == itEnd)
    {
        List.push_back (pwnd->m_hWnd);

        // If this is a child window & the next window item has not yet been added
        if ( (pwnd->GetStyle() & WS_CHILD) &&
            (pSysMenu->GetMenuState (SC_NEXTWINDOW, MF_BYCOMMAND) == 0xFFFFFFFF))
        {
            // Windows exhibits odd behavior by always handing us a non-child system menu
            // The text is currently wrong and diaplays "alt-f4" as the shortcut
            // instead of "ctrl-f4". The following code fixes that.
            CString strClose;
            LoadString(strClose, IDS_CLOSE);
            sc = pSysMenu->ModifyMenu( SC_CLOSE, MF_STRING | MF_BYCOMMAND, SC_CLOSE, strClose ) ? S_OK : E_FAIL;
            sc.TraceAndClear();

            // Add a separator
            sc = pSysMenu->AppendMenu (MF_SEPARATOR) ? S_OK : E_FAIL;
            sc.TraceAndClear();

            // Add the "Next" item
            CString strNext;
            LoadString(strNext, IDS_NEXTWINDOW);
            sc = pSysMenu->AppendMenu( MF_STRING, SC_NEXTWINDOW, strNext ) ? S_OK : E_FAIL;
            sc.TraceAndClear();
        }

        sc = pSysMenu->AppendMenu (MF_SEPARATOR) ? S_OK : E_FAIL;
        sc.TraceAndClear();
    }

    int cAppendedItems = 0;

    int     cItemsToAppend = pMenuToAppend->GetMenuItemCount ();
    TCHAR   szMenuText[64];

    MENUITEMINFO    mii;
    mii.cbSize     = sizeof (mii);
    mii.fMask      = MIIM_ID | MIIM_TYPE | MIIM_SUBMENU;
    mii.dwTypeData = szMenuText;

    for (int i = 0; i < cItemsToAppend; i++)
    {
        ASSERT (mii.dwTypeData == szMenuText);
        mii.cch = countof (szMenuText);
        if (! ::GetMenuItemInfo (pMenuToAppend->m_hMenu, i, TRUE, &mii))
            sc.FromLastError().TraceAndClear();

        // this code can't handle cascaded additions to the system menu
        ASSERT (mii.hSubMenu == NULL);

        // if the menu item is a separator or isn't already there, append it
        if ((mii.fType & MFT_SEPARATOR) ||
            (pSysMenu->GetMenuState (mii.wID, MF_BYCOMMAND) == 0xFFFFFFFF))
        {
            pSysMenu->AppendMenu (mii.fType, mii.wID, szMenuText);
            cAppendedItems++;
        }
    }

    // return the number of items appended
    return (cAppendedItems);
}