//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1999 - 1999
//
//  File:       tasksym.cpp
//
//--------------------------------------------------------------------------

#include "stdafx.h"
#include "tasks.h"
#include "stgio.h"
#include <commdlg.h>
#include "symbinfo.h"
#include "pickicon.h"
#include "util.h"


const int NUM_SYMBOLS = (sizeof(s_rgEOTSymbol)/sizeof(s_rgEOTSymbol[0]));

static const int s_cxIcon            = 32;	// size of an icon
static const int s_cxSelectionMargin =  4;	// additional border for selection
static const int s_cxIconGutter      = 10;	// gutter space between icons (keep this even)

//############################################################################
//############################################################################
//
//  Implementation of class CEOTSymbol
//
//############################################################################
//############################################################################

CEOTSymbol::~CEOTSymbol()
{
}

bool
CEOTSymbol::operator == (const CEOTSymbol &rhs)
{
    return ( (m_iconResource  == rhs.m_iconResource) &&
             (m_value == rhs.m_value) &&
             (m_ID    == rhs.m_ID) );

}

void
CEOTSymbol::SetIcon(const CSmartIcon & smartIconSmall, const CSmartIcon & smartIconLarge)
{
    m_smartIconSmall = smartIconSmall;
    m_smartIconLarge = smartIconLarge;
}



/*+-------------------------------------------------------------------------*
 *
 * CEOTSymbol::Draw
 *
 * PURPOSE:
 *
 * PARAMETERS:
 *    HDC    hdc :
 *    RECT * lpRect :
 *    bool   bSmall :
 *
 * RETURNS:
 *    void
 *
 *+-------------------------------------------------------------------------*/
void
CEOTSymbol::Draw(HDC hdc, RECT *lpRect, bool bSmall) const
{
    // if the icons already exist, Draw has been called before OR this symbol has a custom icon that as been
    // assigned the icons directly using SetIcon

    if((HICON)m_smartIconSmall == NULL)
    {
        m_smartIconSmall.Attach((HICON)::LoadImage(_Module.GetResourceInstance(),
                                                    MAKEINTRESOURCE(m_iconResource), IMAGE_ICON, 16, 16, 0));
    }

    if((HICON)m_smartIconLarge == NULL)
    {
        m_smartIconLarge.Attach((HICON)::LoadImage(_Module.GetResourceInstance(),
                                                    MAKEINTRESOURCE(m_iconResource), IMAGE_ICON, 32, 32, 0));
    }

	/*
	 * Preserve icon shape when BitBlitting it to a
	 * mirrored DC.
	 */
	DWORD dwLayout=0L;
	if ((dwLayout=GetLayout(hdc)) & LAYOUT_RTL)
	{
		SetLayout(hdc, dwLayout|LAYOUT_BITMAPORIENTATIONPRESERVED);
	}

    DrawIconEx(hdc, lpRect->left, lpRect->top, bSmall ? m_smartIconSmall : m_smartIconLarge,
               bSmall? 16 : 32, bSmall? 16 : 32, 0, NULL, DI_NORMAL);

	/*
	 * Restore the DC to its previous layout state.
	 */
	if (dwLayout & LAYOUT_RTL)
	{
		SetLayout(hdc, dwLayout);
	}
}


/*+-------------------------------------------------------------------------*
 *
 * CEOTSymbol::IsMatch
 *
 * PURPOSE: Checks to see whether str1 is one of the strings contained in the
 *          comma separated list str2.
 *
 * PARAMETERS:
 *    CStr & str1 :
 *    CStr & str2 :
 *
 * RETURNS:
 *    bool: true if str1 is contained in str2, else false.
 *
 *+-------------------------------------------------------------------------*/
bool
CEOTSymbol::IsMatch(CStr &str1, CStr &str2)
{
    // trim spaces off either end.
    str1.TrimLeft();
    str1.TrimRight();

    CStr strTemp;
    int length;
    while((length = str2.GetLength()) != 0 )
    {
        int index = str2.Find(TEXT(','));
        if(index!=-1)
        {
            strTemp = str2.Left(index); // index is the pos of the ',' so we're OK.
            str2 = str2.Right(length - index -1);
        }
        else
        {
            strTemp = str2;
            str2.Empty();
        }

        strTemp.TrimLeft();
        strTemp.TrimRight();
        // compare str1 and strTemp
        if( str1.CompareNoCase((LPCTSTR)strTemp)==0)
            return true;    // match
    }
    return false;
}

int
CEOTSymbol::FindMatchingSymbol(LPCTSTR szDescription) // finds a symbol matching the given description.
{
    CStr strDescription = szDescription;

    int iSelect = -1;
    for(int i = 0; i<NUM_SYMBOLS; i++)
    {
        CStr strDescTemp;
        int ID = s_rgEOTSymbol[i].GetID();
        strDescTemp.LoadString(_Module.GetResourceInstance(), ID); // get the string.
        if(IsMatch(strDescription, strDescTemp))
        {
            iSelect = i;  // perfect match
            break;
        }

        CStr strDescTemp2;
        int ID2 = s_rgEOTSymbol[i].GetIDSecondary();
        if(ID2)
            strDescTemp2.LoadString(_Module.GetResourceInstance(), ID2); // get the string.
        if(IsMatch(strDescription, strDescTemp2))
        {
            iSelect = i;  // imperfect match, keep trying.
        }
    }

    return iSelect;
}


//############################################################################
//############################################################################
//
//  Implementation of class CTaskSymbolDialog
//
//############################################################################
//############################################################################

CTaskSymbolDlg::CTaskSymbolDlg(CConsoleTask& rConsoleTask, bool bFindMatchingSymbol)
	:	m_ConsoleTask (rConsoleTask),
		m_bCustomIcon (rConsoleTask.HasCustomIcon())
{
    m_bFindMatchingSymbol = bFindMatchingSymbol;
}


LRESULT CTaskSymbolDlg::OnInitDialog(UINT mMsg, WPARAM wParam, LPARAM lParam, BOOL& handled)
{
    m_listGlyphs    = GetDlgItem (IDC_GLYPH_LIST);
	m_wndCustomIcon = GetDlgItem (IDC_CustomIcon);

    m_imageList.Create (16, 28, ILC_COLOR , 20, 10);
	m_listGlyphs.SetImageList((HIMAGELIST) m_imageList, LVSIL_NORMAL);
	
	int cxIconSpacing = s_cxIcon + s_cxIconGutter;
    m_listGlyphs.SetIconSpacing (cxIconSpacing, cxIconSpacing);

    int iSelect = 0;

    // insert all the items
    for(int i=0; i< NUM_SYMBOLS; i++)
    {
        LV_ITEM item;
        ZeroMemory(&item, sizeof(item));
        item.mask    = LVIF_PARAM;
        item.lParam  = i;


        if(s_rgEOTSymbol[i].GetValue()==m_ConsoleTask.GetSymbol())
        {
            iSelect    = i;
        }

        m_listGlyphs.InsertItem(&item);
    }

	/*
	 * check the appropriate radio button
	 */
	int nCheckedButton = (m_bCustomIcon) ? IDC_CustomIconRadio : IDC_MMCIconsRadio;
	CheckRadioButton (IDC_CustomIconRadio, IDC_MMCIconsRadio, nCheckedButton);
	SC scNoTrace = ScEnableControls (nCheckedButton);

	/*
	 * if this task has a custom icon, initialize the preview control
	 */
	if (m_bCustomIcon)
		m_wndCustomIcon.SetIcon (m_ConsoleTask.GetLargeCustomIcon());


    if(m_bFindMatchingSymbol) // a description string was passed in, use it to populate the page.
    {
        tstring strName = m_ConsoleTask.GetName();
        if(strName.length()>0)
            iSelect = CEOTSymbol::FindMatchingSymbol((LPCTSTR)strName.data());
    }


	/*
	 * select the icon for this task
	 */
    LV_ITEM item;
    ZeroMemory(&item, sizeof(item));
    item.iItem     = iSelect;
    item.mask      = LVIF_STATE;
    item.state     = LVIS_FOCUSED | LVIS_SELECTED;
    item.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
    m_listGlyphs.SetItem(&item);
    m_listGlyphs.EnsureVisible(iSelect, 0);

    return 0;
}


/*+-------------------------------------------------------------------------*
 * CTaskSymbolDlg::OnCtlColorStatic
 *
 * WM_CTLCOLORSTATIC handler for CTaskSymbolDlg.
 *--------------------------------------------------------------------------*/

LRESULT CTaskSymbolDlg::OnCtlColorStatic(UINT mMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	HBRUSH hbrRet = NULL;

	switch (::GetDlgCtrlID (((HWND) lParam)))
	{
		/*
		 * for the custom icon preview window and its well, if we're using a
		 * custom icon, return a COLOR_WINDOW brush so the static won't paint
		 * the background with COLOR_3DFACE
		 */
		case IDC_CustomIcon:
		case IDC_CustomIconWell:
			if (m_bCustomIcon)
				hbrRet = GetSysColorBrush (COLOR_WINDOW);
			break;
	}

	/*
	 * if we didn't supply a brush, let this message go through to DefWindowProc
	 */
	if (hbrRet == NULL)
		bHandled = false;

	return ((LPARAM) hbrRet);
}


/*+-------------------------------------------------------------------------*
 * CTaskSymbolDlg::OnIconSourceChanged
 *
 * BN_CLICKED handler for CTaskSymbolDlg.
 *--------------------------------------------------------------------------*/

LRESULT CTaskSymbolDlg::OnIconSourceChanged(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled )
{
	m_bCustomIcon = (wID == IDC_CustomIconRadio);
	SC scNoTrace = ScEnableControls (wID);
	return (0);
}


/*+-------------------------------------------------------------------------*
 * CTaskSymbolDlg::ScEnableControls
 *
 * Enables the controls belonging to a particular radio button on the
 * symbol dialog
 *--------------------------------------------------------------------------*/

SC CTaskSymbolDlg::ScEnableControls (int id)
{
	DECLARE_SC (sc, _T("CTaskSymbolDlg::ScEnableControls"));

	/*
	 * validate input
	 */
	ASSERT ((id == IDC_CustomIconRadio) || (id == IDC_MMCIconsRadio));
	if (!  ((id == IDC_CustomIconRadio) || (id == IDC_MMCIconsRadio)))
		return (sc = E_INVALIDARG);

	/*
	 * controls to be enabled when "Custom Icon" radio button is selected
	 */
	static const int nCustomIconCtlIDs[] = {
		IDC_CustomIcon,
		IDC_CustomIconWell,
		IDB_SELECT_TASK_ICON,
		0	// terminator
	};

	/*
	 * controls to be enabled when "MMC Icons" radio button is selected
	 */
	static const int nMMCIconCtlIDs[] = {
        IDC_GLYPH_LIST,
        IDC_DESCRIPTION,
        IDC_DESCRIPTION2,
        IDC_DESCRIPTIONLabel,
        IDC_DESCRIPTION2Label,
		0	// terminator
	};

	const int* pnEnableIDs  = NULL;
	const int* pnDisableIDs = NULL;

	/*
	 * pick the right sets of controls to enable/disable
	 */
	if (id == IDC_CustomIconRadio)
	{
		pnEnableIDs  = nCustomIconCtlIDs;
		pnDisableIDs = nMMCIconCtlIDs;
	}
	else
	{
		pnEnableIDs  = nMMCIconCtlIDs;
		pnDisableIDs = nCustomIconCtlIDs;
	}

	/*
	 * enable/disable the controls
	 */
	for (int i = 0; pnEnableIDs[i] != 0; i++)
		::EnableWindow (GetDlgItem (pnEnableIDs[i]), true);

	for (int i = 0; pnDisableIDs[i] != 0; i++)
		::EnableWindow (GetDlgItem (pnDisableIDs[i]), false);

	return (sc);
}


LRESULT
CTaskSymbolDlg::OnSymbolChanged(int id, LPNMHDR pnmh, BOOL& bHandled )
{
    NMLISTVIEW* pnmlv = (NMLISTVIEW *) pnmh;
    if(! ((pnmlv->uNewState & LVNI_FOCUSED) && (pnmlv->iItem !=-1)) )
        return 0;

    int nItem = pnmlv->iItem;

    CStr strDescription;
    int ID = s_rgEOTSymbol[nItem].GetID();
    strDescription.LoadString(_Module.GetResourceInstance(), ID); // get the string.
    SetDlgItemText(IDC_DESCRIPTION, (LPCTSTR) strDescription);

    CStr strDescription2;
    int ID2 = s_rgEOTSymbol[nItem].GetIDSecondary();
    if(ID2)
        strDescription2.LoadString(_Module.GetResourceInstance(), ID2); // get the string.
    SetDlgItemText(IDC_DESCRIPTION2, (LPCTSTR) strDescription2);

    return 0;
}


LRESULT
CTaskSymbolDlg::OnCustomDraw(int id, LPNMHDR pnmh, BOOL& bHandled )
{
    NMCUSTOMDRAW* pnmcd = (NMCUSTOMDRAW *) pnmh;

    switch(pnmcd->dwDrawStage & ~CDDS_SUBITEM)
    {
		case CDDS_PREPAINT:         // the initial notification
			return CDRF_NOTIFYITEMDRAW;    // we want to know about each item's paint.
	
		case CDDS_ITEMPREPAINT:
			DrawItem(pnmcd);
			return CDRF_SKIPDEFAULT;      // we've drawn the whole item ourselves
	
		default:
			return 0;
    }
}

void
CTaskSymbolDlg::DrawItem(NMCUSTOMDRAW *pnmcd)
{
    DECLARE_SC(sc, TEXT("CTaskSymbolDlg::DrawItem"));

    int  nItem = pnmcd->dwItemSpec;
    HDC  &hdc  = pnmcd->hdc;

    LV_ITEM item;
    ZeroMemory(&item, sizeof(item));
    item.iItem = nItem;
    item.mask  = LVIF_STATE;
    item.stateMask = (UINT) -1; //get all the state bits.
    m_listGlyphs.GetItem(&item);


	/*
	 * get the icon rect for the item and offset it downward by the size
	 * of our border margin
	 */
    RECT rectIcon;
    m_listGlyphs.GetItemRect(nItem, &rectIcon, LVIR_ICON);
	OffsetRect (&rectIcon, 0, s_cxSelectionMargin);

	/*
	 * Make a slightly inflated copy the icon rectangle to draw in the
	 * selection color.  We inflate to make the selection stand out a little
	 * more for large icons.
	 */
	RECT rectBackground = rectIcon;
	InflateRect (&rectBackground, s_cxSelectionMargin, s_cxSelectionMargin);

    bool bWindowHasFocus = (GetFocus() == (HWND)m_listGlyphs);
    bool bSelected       = item.state & LVIS_SELECTED;
	bool bDisabled       = !m_listGlyphs.IsWindowEnabled();

    // Create the select rectangle or empty the rectangle.
	int nBackColorIndex = (bDisabled) ? COLOR_3DFACE	:
						  (bSelected) ? COLOR_HIGHLIGHT	:
										COLOR_WINDOW;

	FillRect (hdc, &rectBackground, (HBRUSH) LongToHandle(nBackColorIndex+1));

    // draw the symbol icon
    s_rgEOTSymbol[nItem].Draw(hdc, &rectIcon);

    if(bWindowHasFocus && bSelected)
        ::DrawFocusRect(hdc, &rectBackground);

    //ReleaseDC(hdc);   DONT release the DC!
}

BOOL
CTaskSymbolDlg::OnOK()
{
    int nItem = m_listGlyphs.GetSelectedIndex();

	/*
	 * make sure we've selected an item
	 */
    if (( m_bCustomIcon && (m_CustomIconLarge == NULL)) ||
		(!m_bCustomIcon && (nItem == -1)))
    {
        CStr strError;
        strError.LoadString(GetStringModule(), IDS_SYMBOL_REQUIRED);
        MessageBox(strError, NULL, MB_OK | MB_ICONEXCLAMATION);
        return (false);
    }

	if (m_bCustomIcon)
		m_ConsoleTask.SetCustomIcon(m_CustomIconSmall, m_CustomIconLarge);
	else
		m_ConsoleTask.SetSymbol(s_rgEOTSymbol[nItem].GetValue());

    return TRUE;
}

/*+-------------------------------------------------------------------------*
 *
 * CTaskSymbolDlg::OnSelectTaskIcon
 *
 * PURPOSE: Uses the shell-provided icon picker dialog to allow the user to select
 *          a custom icon for the console task.
 *
 * PARAMETERS:
 *    WORD  wNotifyCode :
 *    WORD  wID :
 *    HWND  hWndCtl :
 *    BOOL& bHandled :
 *
 * RETURNS:
 *    LRESULT
 *
 *+-------------------------------------------------------------------------*/
LRESULT
CTaskSymbolDlg::OnSelectTaskIcon(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled )
{
    DECLARE_SC(sc, TEXT("CTaskSymbolDlg::OnSelectTaskIcon"));

	static CStr s_strCustomIconFile;
	static int  s_nIconIndex = 0;

    int nIconIndex = s_nIconIndex;
    TCHAR szIconFile[MAX_PATH];

	/*
	 * shouldn't get here unless we think we're using a custom icon
	 */
	ASSERT (m_bCustomIcon);

	/*
	 * reuse the last custom icon source; if it's not available,
	 * default to mmc.exe
	 */
	if (s_strCustomIconFile.IsEmpty())
	{
		LPTSTR pszCustomIconFile = s_strCustomIconFile.GetBuffer (MAX_PATH);
		sc = ScCheckPointers (pszCustomIconFile, E_OUTOFMEMORY);
		if (sc)
		{
			MMCErrorBox (sc);
			return (0);
		}

		GetModuleFileName (NULL, pszCustomIconFile, MAX_PATH);
		s_strCustomIconFile.ReleaseBuffer();
	}

    lstrcpy (szIconFile, s_strCustomIconFile);

    if (PickIconDlg (m_hWnd, szIconFile, countof (szIconFile), &nIconIndex))
    {
        TCHAR szIconFile2[MAX_PATH];
        ExpandEnvironmentStrings(szIconFile, szIconFile2, MAX_PATH);

		/*
		 * remember the user's selection for next time
		 */
		s_strCustomIconFile = szIconFile;
		s_nIconIndex        = nIconIndex;

        // need to extract and copy the icon rather than use LoadImage, because LoadImage uses a custom icon
        CSmartIcon smartIconTemp;

        smartIconTemp.Attach(::ExtractIcon (_Module.m_hInst, szIconFile2, nIconIndex));
        m_CustomIconSmall.Attach((HICON) ::CopyImage((HICON)smartIconTemp, IMAGE_ICON, 16, 16, LR_COPYFROMRESOURCE));
        m_CustomIconLarge.Attach((HICON) ::CopyImage((HICON)smartIconTemp, IMAGE_ICON, 32, 32, LR_COPYFROMRESOURCE));

		/*
		 * update the custom icon preview window
		 */
		m_wndCustomIcon.SetIcon (m_CustomIconLarge);
		m_wndCustomIcon.InvalidateRect (NULL);
    }

    return 0;
}