/*****************************************************************************
 *
 *  Component:  sndvol32.exe
 *  File:       choice.c
 *  Purpose:    properties dialog box code
 * 
 *  Copyright (c) 1985-1999 Microsoft Corporation
 *
 *****************************************************************************/

#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <commctrl.h>

#include "volids.h"
#include "volumei.h"
#include "utils.h"

typedef struct t_DEVICEPROP {
    BOOL            fMixer;             // is mixer
    UINT            uDeviceID;          // device ID
    DWORD           dwPlayback;         // mixerline representing playback
    DWORD           dwRecording;        // mixerline representing recording
    MIXERCAPS       mxcaps;
} DEVICEPROP, *PDEVICEPROP;

typedef struct t_PRIVPROP {
    PMIXUIDIALOG    pmxud;              // app instance data
    PDEVICEPROP     adp;                // array of allocated device props
    PDEVICEPROP     pdpCurSel;          // current device selection
    DWORD           dwDestSel;          // last destination selection
    
    PVOLCTRLDESC    avcd;
    DWORD           cvcd;
    
    // in/out params

    UINT            uDeviceID;
    BOOL            fMixer;
    DWORD           dwDestination;
    DWORD           dwStyle;
    
} PRIVPROP, *PPRIVPROP;

#define PROPATOM        TEXT("privprop")
const TCHAR gszPropAtom[] = PROPATOM;
#define SETPROP(x,y)    SetProp((x), gszPropAtom, (HANDLE)(y))
#define GETPROP(x)      (PPRIVPROP)GetProp((x), gszPropAtom)
#define REMOVEPROP(x)   RemoveProp(x,gszPropAtom)

void SetCheckState (HWND hwndLV, int nItem, BOOL bCheck)
{
    LVITEM item;
	int nCheck = bCheck ? 2 : 1;	// one based index

    ZeroMemory (&item, sizeof (item));
    item.mask = LVIF_STATE;
    item.iItem = nItem;
    item.stateMask = LVIS_STATEIMAGEMASK;
    item.state = INDEXTOSTATEIMAGEMASK (nCheck);
    ListView_SetItem (hwndLV, &item);
}

void EnableOk(
    HWND            hwnd,
    BOOL            fEnable)
{
    HWND    hOk, hCancel;
    BOOL    fWasEnabled;
    
    hOk     = GetDlgItem(hwnd,IDOK);
    hCancel = GetDlgItem(hwnd,IDCANCEL);
    
    fWasEnabled = IsWindowEnabled(hOk);

    if (!fEnable && fWasEnabled)
    {
        Button_SetStyle(hOk, BS_PUSHBUTTON, TRUE);
        SendMessage(hwnd, DM_SETDEFID, IDCANCEL, 0L);
        EnableWindow(hOk, FALSE);
    }
    else if (fEnable && !fWasEnabled)
    {
        EnableWindow(hOk, TRUE);
        Button_SetStyle(hCancel, BS_PUSHBUTTON, TRUE);
        SendMessage(hwnd, DM_SETDEFID, IDOK, 0L);
    }
}

void Properties_GroupEnable(
    HWND            hwnd,
    const int       ids[],
    int             cids,
    BOOL            fEnable)
{
    int i;
    for (i = 0; i < cids; i++)
        EnableWindow(GetDlgItem(hwnd, ids[i]), fEnable);
}
    
void Properties_Enable_Prop_Volumes(
    HWND            hwnd,
    BOOL            fEnable)
{
    const int ids[] = {
        IDC_PROP_VOLUMES,
        IDC_PROP_PLAYBACK,
        IDC_PROP_RECORDING,
        IDC_PROP_OTHER,
        IDC_PROP_OTHERLIST
    };
    if (!fEnable)
        ComboBox_SetCurSel(GetDlgItem(hwnd, IDC_PROP_OTHERLIST), -1);
    Properties_GroupEnable(hwnd, ids, SIZEOF(ids), fEnable);
}

void Properties_Enable_PROP_DEVICELIST(
    HWND            hwnd,
    BOOL            fEnable)
{
    const int ids[] = {
        IDC_PROP_TXT1,
        IDC_PROP_DEVICELIST,
    };
    if (!fEnable)
        ComboBox_SetCurSel(GetDlgItem(hwnd, IDC_PROP_OTHERLIST), -1);
    Properties_GroupEnable(hwnd, ids, SIZEOF(ids), fEnable);
}

void Properties_Enable_PROP_VOLUMELIST(
    HWND            hwnd,
    BOOL            fEnable)
{
    const int ids[] = {
        IDC_PROP_TXT2,
        IDC_PROP_VOLUMELIST,
    };
    Properties_GroupEnable(hwnd, ids, SIZEOF(ids), fEnable);
}


void Properties_CommitState(
    PPRIVPROP       ppr,
    HWND            hlb)
{
    DWORD           i;
    DWORD           cItems = ListView_GetItemCount(hlb);
    
    if (!ppr->avcd)
        return;
               
    for (i=0;i<cItems;i++)
    {
        LV_ITEM         lvi;
        
        lvi.iItem       = i;
        lvi.iSubItem    = 0;
        lvi.mask        = LVIF_PARAM;
        
        if (ListView_GetItem(hlb, &lvi))
        {
            ppr->avcd[lvi.lParam].dwSupport = (ListView_GetCheckState (hlb, i))?0:VCD_SUPPORTF_HIDDEN;
        }
    }
            
    Volume_GetSetRegistryLineStates(ppr->pdpCurSel->mxcaps.szPname
                                    , ppr->avcd[0].szShortName
                                    , ppr->avcd
                                    , ppr->cvcd
                                    , SET );

    Mixer_CleanupVolumeDescription(ppr->avcd, ppr->cvcd);

    GlobalFreePtr(ppr->avcd);
    ppr->avcd = NULL;
    ppr->cvcd = 0L;
}
    

/*
 * Init the destination type groupbox
 * 
 * */
BOOL Properties_Init_Prop_Volumes(
    PPRIVPROP       ppr,
    HWND            hwnd)
{
    HWND            hPlay = GetDlgItem(hwnd, IDC_PROP_PLAYBACK);
    HWND            hRec = GetDlgItem(hwnd, IDC_PROP_RECORDING);
    HWND            hOther = GetDlgItem(hwnd, IDC_PROP_OTHER);
    HWND            hOtherList = GetDlgItem(hwnd, IDC_PROP_OTHERLIST);
    DWORD           iDest, cDest;
    BOOL            fPlay = FALSE, fRec = FALSE;
    
    if (!ppr->pdpCurSel->fMixer)
        return FALSE;

    ComboBox_ResetContent(hOtherList);

    cDest = ppr->pdpCurSel->mxcaps.cDestinations;
    
    EnableWindow(hPlay, FALSE);
    EnableWindow(hRec, FALSE);
    EnableWindow(hOther, FALSE);
    
    for (iDest = 0; iDest < cDest; iDest++)
    {
        MIXERLINE   mlDst;
        int         imx;
        
        mlDst.cbStruct      = sizeof ( mlDst );
        mlDst.dwDestination = iDest;

        if (mixerGetLineInfo((HMIXEROBJ)UIntToPtr(ppr->pdpCurSel->uDeviceID)
                             , &mlDst
                             , MIXER_GETLINEINFOF_DESTINATION)
            != MMSYSERR_NOERROR)
            continue;

        //
        // Conditionally enable selections.  The first type for Play and
        // Record are the default Playback and Recording radiobuttons.
        // The next occurence of the same type is heaped into the Other
        // category.
        //

        if (mlDst.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
            && !fPlay)
        {
            EnableWindow(hPlay, TRUE);

            if (iDest == ppr->dwDestSel)
                CheckRadioButton(hwnd, IDC_PROP_PLAYBACK, IDC_PROP_OTHER
                                 , IDC_PROP_PLAYBACK);
            ppr->pdpCurSel->dwPlayback = iDest;
            fPlay = TRUE;
        }
        else if (mlDst.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN
                 && !fRec)
        {
            EnableWindow(hRec, TRUE);

            if (iDest == ppr->dwDestSel)
                CheckRadioButton(hwnd, IDC_PROP_PLAYBACK, IDC_PROP_OTHER
                                 , IDC_PROP_RECORDING);
            ppr->pdpCurSel->dwRecording = iDest;
            fRec = TRUE;
        }
        else
        {
            EnableWindow(hOther, TRUE);
            imx = ComboBox_AddString(hOtherList, mlDst.szName);
            ComboBox_SetItemData(hOtherList, imx, iDest);

            if (iDest == ppr->dwDestSel)
            {
                CheckRadioButton(hwnd, IDC_PROP_PLAYBACK, IDC_PROP_OTHER
                                 , IDC_PROP_OTHER);
                ComboBox_SetCurSel(hOtherList, imx);
            }
        }
    }

    //
    // Disable the "other" drop down if its not selected
    //
    if (!IsDlgButtonChecked(hwnd, IDC_PROP_OTHER))
    {
        ComboBox_SetCurSel(hOtherList, 0);
        EnableWindow(hOtherList, FALSE);
    }
            
    return TRUE;
}
    
BOOL Properties_Init_PROP_VOLUMELIST(
    PPRIVPROP       ppr,
    HWND            hwnd)
{
    HWND            hlb = GetDlgItem(hwnd, IDC_PROP_VOLUMELIST);
    LV_COLUMN       col = {LVCF_FMT | LVCF_WIDTH, LVCFMT_LEFT};
    RECT            rc;
    BOOL            fSet = FALSE;
    PVOLCTRLDESC    avcd = NULL;
    DWORD           cvcd = 0L;
    DWORD           ivcd;
    int             ilvi;
    LV_ITEM         lvi;
        
    
    ListView_DeleteAllItems(hlb);
    ListView_SetExtendedListViewStyleEx (hlb, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
    ZeroMemory (&lvi, sizeof (lvi));

    GetClientRect(hlb, &rc);
    col.cx = rc.right - 2 * GetSystemMetrics(SM_CXEDGE);
    ListView_InsertColumn(hlb, 0, &col);

    if (ppr->avcd)
    {
        if (ppr->pdpCurSel->fMixer)
            Mixer_CleanupVolumeDescription(ppr->avcd, ppr->cvcd);
//        else
//            Nonmixer_CleanupVolumeDescription(ppr->avcd, ppr->cvcd);

        GlobalFreePtr(ppr->avcd);
                    
        ppr->avcd = NULL;
        ppr->cvcd = 0L;
    }
            
    if (ppr->pdpCurSel->fMixer)
    {
		HMIXER		hmx;
		MMRESULT	mmr;

		//
		//  Mixer API's are much more efficient with mixer handle...
		//

		mmr = mixerOpen(&hmx
				, ppr->pdpCurSel->uDeviceID
				, 0L
				, 0L
				, MIXER_OBJECTF_MIXER);

		if(MMSYSERR_NOERROR == mmr)
		{
	        avcd = Mixer_CreateVolumeDescription((HMIXEROBJ)hmx
	                                             , ppr->dwDestSel
	                                             , &cvcd);

			mixerClose(hmx);
		}
		else
		{
	        avcd = Mixer_CreateVolumeDescription((HMIXEROBJ)UIntToPtr(ppr->pdpCurSel->uDeviceID)
	                                             , ppr->dwDestSel
	                                             , &cvcd);
		}
    }
    else
    {
        avcd = Nonmixer_CreateVolumeDescription(ppr->dwDestSel
                                                , &cvcd);

    }

    if (avcd)
    {
        BOOL fFirstRun;
        //
        // Restore hidden state 
        //
        fFirstRun = !Volume_GetSetRegistryLineStates(ppr->pdpCurSel->mxcaps.szPname
                                                    , avcd[0].szShortName
                                                    , avcd
                                                    , cvcd
                                                    , GET );

        if (fFirstRun)
        {
            for (ivcd = 0; ivcd < cvcd; ivcd++)
            {
                if (!(avcd[ivcd].dwSupport & VCD_SUPPORTF_DEFAULT))
                    avcd[ivcd].dwSupport |= VCD_SUPPORTF_HIDDEN;
            }
            Volume_GetSetRegistryLineStates(ppr->pdpCurSel->mxcaps.szPname
                                            , avcd[0].szShortName
                                            , avcd
                                            , cvcd
                                            , SET);
        }
            
        for (ivcd = 0, ilvi = 0; ivcd < cvcd; ivcd++)
        {
            if (!(avcd[ivcd].dwSupport & VCD_SUPPORTF_VISIBLE))
                continue;

            lvi.iItem           = ilvi;
            lvi.iSubItem        = 0;
            lvi.mask            = LVIF_TEXT|LVIF_PARAM;
            lvi.lParam          = ivcd;
            lvi.pszText         = avcd[ivcd].szName;

            ListView_InsertItem(hlb, &lvi);
        
            if (!(avcd[ivcd].dwSupport & VCD_SUPPORTF_HIDDEN))
            {
                fSet = TRUE;
                SetCheckState (hlb, ilvi, TRUE);
            }
            ilvi++;

        }
    
        ppr->avcd = avcd;
        ppr->cvcd = cvcd;
    }
    
    ListView_SetItemState(hlb, 0, TRUE, LVIS_FOCUSED);

    EnableOk(hwnd, fSet);
        
    return TRUE;
}

/*
 * Init the list of devices.
 *
 * */
BOOL Properties_Init_PROP_DEVICELIST(
    PPRIVPROP       ppr,
    HWND            hwnd)
{
    HWND            hlb = GetDlgItem(hwnd, IDC_PROP_DEVICELIST);
    int             iMixer;
    int             cValidMixers = 0;
    int             cMixers;

    cMixers = mixerGetNumDevs();
    if (!cMixers)
        return FALSE;

    ppr->adp = (PDEVICEPROP)GlobalAllocPtr(GHND,cMixers * sizeof(DEVICEPROP));
    if (ppr->adp == NULL)
        return FALSE;
    
    for (iMixer = 0; iMixer < cMixers; iMixer++)
    {
        int         imx;
        MIXERCAPS*  pmxcaps = &ppr->adp[iMixer].mxcaps;
        MMRESULT    mmr;
        
        mmr = mixerGetDevCaps(iMixer, pmxcaps, sizeof(MIXERCAPS));

        if (mmr != MMSYSERR_NOERROR)
            continue;

        cValidMixers++;
        
        imx = ComboBox_AddString(hlb, pmxcaps->szPname);
        
        ppr->adp[iMixer].uDeviceID  = iMixer;
        ppr->adp[iMixer].fMixer     = TRUE;
        
        ComboBox_SetItemData(hlb, imx, &ppr->adp[iMixer]);
    }
    
    if (cValidMixers == 0)
        return FALSE;

    return TRUE;
}

BOOL Properties_OnCommand(
    HWND            hwnd,
    int             id,
    HWND            hctl,
    UINT            unotify)
{
    PPRIVPROP       ppr = GETPROP(hwnd);
    if (!ppr)
        return FALSE;

    switch (id)
    {
        case IDC_PROP_DEVICELIST:
            if (unotify == CBN_SELCHANGE)
            {
                int         imx;
                PDEVICEPROP pdp;
                
                imx = ComboBox_GetCurSel(hctl);
                pdp = (PDEVICEPROP)ComboBox_GetItemData(hctl, imx);

                if (pdp == ppr->pdpCurSel)
                    break;

                ppr->pdpCurSel = pdp;
                
                if (ppr->pdpCurSel->fMixer)
                    Properties_Init_Prop_Volumes(ppr, hwnd);
                else
                    Properties_Enable_Prop_Volumes(hwnd, FALSE);
                Properties_Init_PROP_VOLUMELIST(ppr, hwnd);
            }
            break;
            
        case IDC_PROP_PLAYBACK:
            EnableWindow(GetDlgItem(hwnd, IDC_PROP_OTHERLIST), FALSE);
            if (ppr->dwDestSel == ppr->pdpCurSel->dwPlayback)
                break;
            ppr->dwDestSel = ppr->pdpCurSel->dwPlayback;            
            Properties_Init_PROP_VOLUMELIST(ppr, hwnd);
            break;
            
        case IDC_PROP_RECORDING:
            EnableWindow(GetDlgItem(hwnd, IDC_PROP_OTHERLIST), FALSE);
            if (ppr->dwDestSel == ppr->pdpCurSel->dwRecording)
                break;
            ppr->dwDestSel = ppr->pdpCurSel->dwRecording;
            Properties_Init_PROP_VOLUMELIST(ppr, hwnd);            
            break;
            
        case IDC_PROP_OTHER:
        {
            HWND        hol;
            DWORD       dwSel;
            
            hol = GetDlgItem(hwnd, IDC_PROP_OTHERLIST);
            EnableWindow(hol, TRUE);
            ComboBox_SetCurSel(hol, 0);
            dwSel = (DWORD)ComboBox_GetItemData(hol, 0);
            
            if (ppr->dwDestSel == dwSel)
                break;

            ppr->dwDestSel = dwSel;
            Properties_Init_PROP_VOLUMELIST(ppr, hwnd);
            break;
        }
        case IDC_PROP_OTHERLIST:
            if (unotify == CBN_SELCHANGE)
            {
                int         idst;
                DWORD       dwSel;
                
                idst = ComboBox_GetCurSel(hctl);
                dwSel = (DWORD)ComboBox_GetItemData(hctl, idst);
                if (ppr->dwDestSel == dwSel)
                    break;
                ppr->dwDestSel = dwSel;
                Properties_Init_PROP_VOLUMELIST(ppr, hwnd);
            }
            break;
        case IDOK:
            //
            // Save out to registry for restaring.
            //
            Properties_CommitState(ppr, GetDlgItem(hwnd, IDC_PROP_VOLUMELIST));

            ppr->uDeviceID      = ppr->pdpCurSel->uDeviceID;
            ppr->fMixer         = TRUE;
            ppr->dwDestination  = ppr->dwDestSel;
            EndDialog(hwnd, TRUE);
            break;
            
        case IDCANCEL:
            Mixer_CleanupVolumeDescription(ppr->avcd, ppr->cvcd);
            if (ppr->avcd)
            {
                GlobalFreePtr(ppr->avcd);
                ppr->avcd = NULL;
                ppr->cvcd = 0L;
            }
            EndDialog(hwnd, FALSE);
            break;
    }
    return FALSE;
}


/*
 * Initialize the dialog
 *
 * */
BOOL Properties_OnInitDialog(
    HWND            hwnd,
    HWND            hwndFocus,
    LPARAM          lParam)
{
    PPRIVPROP       ppr;
    BOOL            fEnable = TRUE;
    HWND            hlv;
    
    SETPROP(hwnd, (PPRIVPROP)lParam);
    
    ppr = GETPROP(hwnd);
    
    if (!ppr)
        fEnable = FALSE;
    
    if (fEnable)
        fEnable = Properties_Init_PROP_DEVICELIST(ppr, hwnd);

    hlv = GetDlgItem(hwnd, IDC_PROP_VOLUMELIST);
    
    if (!fEnable)
    {
        //
        // Bad state, disable everything except cancel
        //
        Properties_Enable_PROP_DEVICELIST(hwnd, FALSE);        
        Properties_Enable_Prop_Volumes(hwnd, FALSE);
        Properties_Enable_PROP_VOLUMELIST(hwnd, FALSE);
        EnableOk(hwnd, FALSE);
        return FALSE;
    }
    else
    {
        int             i;
        PDEVICEPROP     pdp;
        HWND            hdl;

        //
        // make intial device selection
        //
        hdl = GetDlgItem(hwnd, IDC_PROP_DEVICELIST);

        ppr->dwDestSel = 0;
        ComboBox_SetCurSel(hdl, 0);
        ppr->pdpCurSel = (PDEVICEPROP)ComboBox_GetItemData(hdl, 0);
        i = ComboBox_GetCount(hdl);
        
        for (; i > 0 ; i-- )
        {
            pdp = (PDEVICEPROP)ComboBox_GetItemData(hdl,i-1);
            //
            // if things match up, then set the init data
            //
            if (pdp->uDeviceID == ppr->uDeviceID
                && pdp->fMixer == ppr->fMixer)
            {
                ppr->pdpCurSel = pdp;
                ComboBox_SetCurSel(hdl, i-1);
                ppr->dwDestSel = ppr->dwDestination;
                break;
            }
        }
    }

    Properties_Init_Prop_Volumes(ppr, hwnd);
    Properties_Init_PROP_VOLUMELIST(ppr, hwnd);
    
    return FALSE;
}



INT_PTR CALLBACK Properties_Proc(
    HWND            hwnd,
    UINT            msg,
    WPARAM          wparam,
    LPARAM          lparam)
{
    extern TCHAR gszHelpFileName[];
#include "helpids.h"
    static const DWORD aHelpIds[] = {
        IDC_PROP_TXT1,          IDH_SNDVOL32_SELECT_DEVICE,
        IDC_PROP_DEVICELIST,    IDH_SNDVOL32_SELECT_DEVICE,
        IDC_PROP_VOLUMES,       IDH_SNDVOL32_SELECT_SOUND,
        IDC_PROP_PLAYBACK,      IDH_SNDVOL32_SELECT_SOUND,
        IDC_PROP_RECORDING,     IDH_SNDVOL32_SELECT_SOUND,
        IDC_PROP_OTHER,         IDH_SNDVOL32_SELECT_SOUND,
        IDC_PROP_OTHERLIST,     IDH_SNDVOL32_SELECT_SOUND,
        IDC_PROP_TXT2,          IDH_SNDVOL32_VOLCONTROL,
        IDC_PROP_VOLUMELIST,    IDH_SNDVOL32_VOLCONTROL,
        0,                      0
    };
    switch (msg)
    {
        case WM_CONTEXTMENU:
            WinHelp((HWND)wparam, gszHelpFileName, HELP_CONTEXTMENU,
                (UINT_PTR)(LPSTR)aHelpIds);
            break;
            
        case WM_HELP:
        {
            LPHELPINFO lphi = (LPVOID) lparam;
            WinHelp (lphi->hItemHandle, gszHelpFileName, HELP_WM_HELP,
                (UINT_PTR) (LPSTR) aHelpIds);
            return TRUE;
        }            
                
        case WM_INITDIALOG:
            HANDLE_WM_INITDIALOG(hwnd, wparam, lparam, Properties_OnInitDialog);
            return TRUE;
            
        case WM_COMMAND:
            HANDLE_WM_COMMAND(hwnd, wparam, lparam, Properties_OnCommand);
            break;

        case WM_CLOSE:
            EndDialog(hwnd, FALSE);
            break;

        case WM_DESTROY:
        {
            PPRIVPROP ppr = GETPROP(hwnd);
            if (ppr)
            {
                if (ppr->adp)
                {
                    GlobalFreePtr(ppr->adp);
                    ppr->adp = NULL;
                }
                REMOVEPROP(hwnd);
            }
            break;
        }

        case WM_NOTIFY:
        {
            NMHDR FAR* lpnm = (NMHDR FAR *)lparam;
            if ((LVN_ITEMCHANGED == lpnm->code) && hwnd)
            {
                HWND hwndLV = GetDlgItem (hwnd, IDC_PROP_VOLUMELIST);
                if (hwndLV)
                {
                    int nItems = ListView_GetItemCount (hwndLV);
                    int indx;

                    for (indx = 0; indx < nItems; indx++)
                        if (ListView_GetCheckState (hwndLV, indx))
                            break;

                    EnableOk (hwnd, nItems != indx);
                }
			}
            break;
        }
    }
    return FALSE;
}

BOOL Properties(
    PMIXUIDIALOG    pmxud,
    HWND            hwnd)
{
    INT_PTR         iret;
    PRIVPROP        pr;

    ZeroMemory(&pr, sizeof(pr));
    pr.dwDestination = pmxud->iDest;
    pr.uDeviceID     = pmxud->mxid;
    pr.fMixer        = pmxud->dwFlags & MXUD_FLAGSF_MIXER;
    pr.dwStyle       = pmxud->dwStyle;
                      
    pr.pmxud         = pmxud;

    iret = DialogBoxParam(pmxud->hInstance
                          , MAKEINTRESOURCE(IDD_PROPERTIES)
                          , hwnd
                          , Properties_Proc
                          , (LPARAM)(LPVOID)&pr );

    if (iret == TRUE)
    {
        pmxud->mxid     = pr.uDeviceID;
        pmxud->iDest    = pr.dwDestination;
        pmxud->dwFlags  &= ~MXUD_FLAGSF_MIXER;
        pmxud->dwFlags  |= (pr.fMixer)?MXUD_FLAGSF_MIXER:0L;
        pmxud->dwStyle  = pr.dwStyle;
    }    

    return (iret == -1) ? FALSE : TRUE;
}