///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  MAIN.CPP
//
//      Main window of multimedia framework
//
//      Copyright (c) Microsoft Corporation     1997
//    
//      12/14/97 David Stewart / dstewart
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <TCHAR.H>
#include "resource.h"
#include "objbase.h"
#include "initguid.h"
#include "sink.h"
#include "dib.h"
#include "resource.h"
#include "mbutton.h"  
#include "knob.h"
#include "winuser.h"
#include "img.h"
#include "frame.h"
#include <htmlhelp.h>
#include "..\cdopt\cdopt.h"
#include "..\cdnet\cdnet.h"
#include "mmenu.h"
#include <stdio.h>
#include "shellico.h"
#include <shellapi.h>
#include "..\cdplay\playres.h"
#include "wininet.h"

//Support for new WM_DEVICECHANGE behaviour in NT5
/////////////////////////////////////////////////
#include <objbase.h>
#include <setupapi.h>
#include <cfgmgr32.h>
#include <initguid.h>
#include <dbt.h>
#include <devguid.h>
#include <mmddkp.h>
#include <ks.h>
#include <ksmedia.h>

HDEVNOTIFY DeviceEventContext = NULL;

void Volume_DeviceChange_Init(HWND hWnd, DWORD dwMixerID);
void Volume_DeviceChange_Cleanup();
void Volume_DeviceChange(HWND hDlg, WPARAM wParam, LPARAM lParam);

//////////////////////////////////////////////////////
//////////////////////////////////////////////////////


// Next 2 lines added to add multimon support
#define COMPILE_MULTIMON_STUBS
#include "multimon.h"

////////////////////////////////////////////////////////////////////////////////////////////
// #defines for main ui and call-downs to cd player unit

#define IDC_LEDWINDOW                   IDC_LED
#define WM_LED_INFO_PAINT               (WM_USER+2000) //wparam = bool (allow self-draw), lparam = vol info
#define WM_LED_MUTE                     (WM_USER+2001) //wparam = unused, lparam = bool (mute)
#define WM_LED_DOWNLOAD                 (WM_USER+2002) //wparam = unused, lparam = download flag

//command ids from cdplayer
#define ID_CDUPDATE                     IDM_NET_CD

//battery power limit, stated as a percentage
#define BATTERY_PERCENTAGE_LIMIT        10

//helpers for detecting where the mouse is hitting
#define TITLEBAR_HEIGHT                 15
#define TITLEBAR_YOFFSET_LARGE          7
#define TITLEBAR_YOFFSET_SMALL          4
#define SYSMENU_XOFFSET                 7
#define SYSMENU_WIDTH                   12

//volume bar timer stuff
#define VOLUME_PERSIST_TIMER_RATE       2000
#define VOLUME_PERSIST_TIMER_EVENT      1000
#define SYSTIMERID                      1001

//don't remove the parens on these, or My Dear Aunt Sally will getcha
#define IDM_HOMEMENU_BASE               (LAST_SEARCH_MENU_ID + 1)
#define IDM_NETMENU_BASE                (LAST_SEARCH_MENU_ID + 100)
#define IDM_TRACKLIST_BASE              10000
#define IDM_DISCLIST_BASE               20000

#define TYPICAL_DISPLAY_AREA            48  //this value is the offset for large fonts
#define EDGE_CURVE_WIDTH                24
#define EDGE_CURVE_HEIGHT               26

#define VENDORLOGO_WIDTH                44
#define VENDORLOGO_HEIGHT               22
#define LOGO_Y_OFFSET                   10

//if button is re-hit within the time limit, don't allow it to trigger
#define MENU_TIMER_RATE 400

//ie autosearch url
#define REG_KEY_SEARCHURL TEXT("Software\\Microsoft\\Internet Explorer\\SearchUrl")
#define REG_KEY_SHELLSETTINGS REG_KEY_NEW_FRAMEWORK TEXT("\\Settings")
#define REG_KEY_SHELLENABLE TEXT("Tray")
#define PLAYCOMMAND1 TEXT("/play")
#define PLAYCOMMAND2 TEXT("-play")
#define TRAYCOMMAND1 TEXT("/tray")
#define TRAYCOMMAND2 TEXT("-tray")

//////////////////////////////////////////////////////////////////////////////////////
// Gradient stuff

#ifndef SPI_GETGRADIENTCAPTIONS
//from nt50 version of winuser.h
#define SPI_GETGRADIENTCAPTIONS             0x1008
#define COLOR_GRADIENTACTIVECAPTION     27
#define COLOR_GRADIENTINACTIVECAPTION   28
#endif

typedef BOOL (WINAPI *GRADIENTPROC)(HDC,PTRIVERTEX,ULONG,PUSHORT,ULONG,ULONG);


////////////////////////////////////////////////////////////////////////////////////////////
// Main functions in this file, forward-declared
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
BOOL LoadComponents(void);
void AddComponent(IMMComponent*);
void CleanUp(void);
void InitComponents(HWND);
BOOL CreateToolTips(HWND);

////////////////////////////////////////////////////////////////////////////////////////////
// Globals to this file

//Component information ... we only have one component now ... 
//this is in here to handle eventual move to multi-component design
PCOMPNODE pCompList = NULL;             //head of component list
PCOMPNODE pCompListTail = NULL;         //tail of component list
PCOMPNODE pNodeCurrent = NULL;          //currently selected component
int nNumComps = 0;                      //number of components
HWND hwndCurrentComp = NULL;            //window handle of current component

HINSTANCE hInst = NULL;                 //global instance of exe
int g_nColorMode = COLOR_VERYHI;        //global containing color mode (hi contract, 16 color, etc)

HWND hwndMain = NULL;                   //main window handle
HWND g_hwndTT = NULL;                   //tooltips
HHOOK g_hhk = NULL;                     //tooltips message hook
TCHAR g_tooltext[MAX_PATH];             //tooltip text holder
HANDLE hbmpMain = NULL;                 //main window bitmap, normal size
HANDLE hbmpMainRestore = NULL;          //main window bitmap, restored size
HANDLE hbmpMainSmall = NULL;            //main window bitmap, small size
HANDLE hbmpMainNoBar = NULL;            //main window bitmap, normal with no title bar
BITMAP bmMain;                          //bitmap metrics for normal size
BITMAP bmMainRestore;                   //bitmap metrics for restored size
BITMAP bmMainSmall;                     //bitmap metrics for small size
BITMAP bmMainNoBar;                     //bitmap metrics for no bar size
HPALETTE hpalMain = NULL;               //Palette of application
BOOL fPlaying = FALSE;                  //Play state of CD for play/pause
BOOL fIntro   = FALSE;                  //Intro mode state
BOOL fShellMode = FALSE;                //are we in shell icon mode?
int nCDMode = IDM_MODE_NORMAL;          //Current mode of CD (starts on normal mode)
int nDispAreaOffset = 0;                //Display area offset for large font mode
CustomMenu* g_pMenu = NULL;             //Pointer to current custom menu
UINT nLastMenu = 0;                      //ID of last button to display a menu
BOOL fBlockMenu = 0;                    //Flag to block menu re-entry
BOOL fOptionsDlgUp = FALSE;             //is options dialog active?
LPCDTITLE pSingleTitle = NULL;            //Disc ID for a direct download from tree control
LPCDOPT g_pOptions = NULL;              //for download callbacks when dialog is up
LPCDDATA g_pData = NULL;                //for callbacks to cd when dialog is up
TCHAR szAppName[MAX_PATH/2];            //IDS_APPNAME "Deluxe CD Player"
DWORD dwLastMixID = (DWORD)-1;          //Last mixer ID
HMIXEROBJ hmix = NULL;                  //current open mixer handle
TCHAR szLineName[MIXER_LONG_NAME_CHARS];//current volume line name
MIXERCONTROLDETAILS mixerlinedetails;   //current volume details
MIXERCONTROLDETAILS mutelinedetails;    //current mute details
DWORD mixervalue[2];                    //current volume level
LONG lCachedBalance = 0;                //last balance level
BOOL fmutevalue;                        //current mute value
HANDLE hMutex = NULL;                   //hMutex to prevent multiple instances of EXE
int  g_nViewMode = VIEW_MODE_NORMAL;    //view mode setting (default to normal)
WORD wDefButtonID = IDB_OPTIONS;         //default button
HCURSOR hCursorMute = NULL;             //mute button cursor
HMODULE hmImage = NULL;                 //module handle of dll with gradient function
GRADIENTPROC fnGradient = NULL;         //gradient function
UINT g_uTaskbarRestart = 0;             //registered message for taskbar re-creation
UINT giVolDevChange = 0;                //registered message for mmsystem device change

#ifdef UNICODE
#define CANONFUNCTION "InternetCanonicalizeUrlW"
#else
#define CANONFUNCTION "InternetCanonicalizeUrlA"
#endif

////////////////////////////////////////////////////////////////////////////////////////////
// Structures and defines for custom button controls
#define NUM_BUTTONS 16
typedef struct BUTTONINFO
{
    int     id;             //id of control
    POINT   uixy;           //x, y location on screen
    POINT   uixy2;          //x, y location when restored or small
    int     width;          //width of control in bitmap and on screen
    int     height;         //height of control in bitmap and on screen
    int     width2;         //width of control on screen when restored
    int     nIconID;        //id of icon, if any
    int     nToolTipID;     //id of tooltip string
    BOOL    fBlockTab;      //true = don't tab stop here
    DWORD   dwStyle;        //style for toolkit, see mbutton.h
} BUTTONINFO, *LPBUTTONINFO;
BUTTONINFO biButtons[NUM_BUTTONS];

////////////////////////////////////////////////////////////////////////////////////////////
// * GetSettings
// Reads the x and y positions of app for startup
// Also gets the view mode
////////////////////////////////////////////////////////////////////////////////////////////
void GetSettings(int& x, int& y)
{
    x = CW_USEDEFAULT;
    y = CW_USEDEFAULT;
    g_nViewMode = VIEW_MODE_NORMAL;

    LPCDOPT pOpt = GetCDOpt();

    if( pOpt )
    {
        LPCDOPTIONS pOptions = pOpt->GetCDOpts();
        LPCDOPTDATA pOptionData = pOptions->pCDData;

        x = pOptionData->dwWindowX;
        y = pOptionData->dwWindowY;
        g_nViewMode = pOptionData->dwViewMode;
        nCDMode = pOptionData->dwPlayMode;
        if (nCDMode < IDM_MODE_NORMAL)
        {
            nCDMode = IDM_MODE_NORMAL;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * SetSettings
// Sets X, Y and view mode of app on shutdown
////////////////////////////////////////////////////////////////////////////////////////////
void SetSettings(int x, int y)
{
    LPCDOPT pOpt = GetCDOpt();

    if(pOpt)
    {
        LPCDOPTIONS pOptions = pOpt->GetCDOpts();
        LPCDOPTDATA pOptionData = pOptions->pCDData;

        pOptionData->dwWindowX = x;
        pOptionData->dwWindowY = y;
        pOptionData->dwViewMode = g_nViewMode;
        pOptionData->dwPlayMode = nCDMode;

        pOpt->UpdateRegistry();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * GetXOffset
// Returns 0 normally, or size of a single border if captions are turned on
////////////////////////////////////////////////////////////////////////////////////////////
int GetXOffset()
{
    #ifndef MMFW_USE_CAPTION
    return 0;
    #else
    return GetSystemMetrics(SM_CXFIXEDFRAME);
    #endif
}

////////////////////////////////////////////////////////////////////////////////////////////
// * GetYOffset
// Returns 0 normally, or size of a single border if captions are turned on
////////////////////////////////////////////////////////////////////////////////////////////
int GetYOffset()
{
    #ifndef MMFW_USE_CAPTION
    return 0;
    #else
    return GetSystemMetrics(SM_CYFIXEDFRAME);
    #endif
}

////////////////////////////////////////////////////////////////////////////////////////////
// * GetYOffsetCaption
// Returns 0 normally, or size of a caption if captions are turned on
////////////////////////////////////////////////////////////////////////////////////////////
int GetYOffsetCaption()
{
    #ifndef MMFW_USE_CAPTION
    return 0;
    #else
    return GetSystemMetrics(SM_CYCAPTION);
    #endif
}

////////////////////////////////////////////////////////////////////////////////////////////
// * DetermineColorMode
// Sets the g_nColorMode variable for use in creating the bumps for the app
////////////////////////////////////////////////////////////////////////////////////////////
void DetermineColorMode()
{
    g_nColorMode = COLOR_VERYHI;

    HDC hdcScreen = GetDC(NULL);
    UINT uBPP = GetDeviceCaps(hdcScreen, PLANES) * GetDeviceCaps(hdcScreen, BITSPIXEL);
    ReleaseDC(NULL, hdcScreen);

    switch (uBPP)
    {
        case 8 :
        {
            g_nColorMode = COLOR_256;
        }
        break;

        case 4 : 
        {
            g_nColorMode = COLOR_16;
        }
        break;

        case 2 :
        {
            g_nColorMode = COLOR_HICONTRAST; 
        }
        break;
    }

    //check directly for accessibility mode
    HIGHCONTRAST hi_con;
    ZeroMemory(&hi_con,sizeof(hi_con));
    hi_con.cbSize = sizeof(hi_con);

    SystemParametersInfo(SPI_GETHIGHCONTRAST,sizeof(hi_con),&hi_con,0);

    if (hi_con.dwFlags &  HCF_HIGHCONTRASTON)
    {
        g_nColorMode = COLOR_HICONTRAST;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * SetPalette
// Sets the palette for the app, generated from all bitmaps in the application and DLLs
////////////////////////////////////////////////////////////////////////////////////////////
HPALETTE SetPalette()
{
    #define NUMPALCOLORS 94

BYTE byVals[NUMPALCOLORS][3] = {
	      6,   6,   6,
	     18,  19,  45,
	     25,  40,   1,
	     17,  46,  46,
	     49,   7,   7,
	     45,  52,   3,
	     49,  49,  49,
	     24,  24,  91,
	      0,  90,   8,
	      1, 108,   5,
	     55,  67,   2,
	     33,  76,  76,
	     94,  29,  24,
	    102,  18, 102,
	     93,  94,  23,
	     78,  78,  78,
	     84,  84, 108,
	     78, 111, 111,
	    109,  80,  80,
	    108, 107,  79,
	    120, 120, 120,
	     15,   9, 157,
	     17,   8, 246,
	     55,  93, 175,
	     20,  91, 231,
	     83,  39, 167,
	     92,  16, 223,
	     96,  94, 150,
	     88,  87, 217,
	      0, 157,  15,
	      0, 153,  51,
	      0, 190,  18,
	     10, 155, 120,
	      6, 252,  17,
	      9, 236,  92,
	    127, 135,  35,
	     83, 142, 117,
	     92, 241,  21,
	     87, 223,  81,
	     14, 171, 171,
	     39, 147, 223,
	     18, 252, 157,
	     15, 244, 236,
	     87, 137, 136,
	     80, 184, 184,
	    112, 141, 141,
	    106, 170, 153,
	    119, 171, 168,
	     87, 155, 213,
	    101, 218, 170,
	     90, 233, 226,
	    154,  19,  24,
	    155,  26, 109,
	    138, 116,   8,
	    170,  98, 101,
	    243,  18,   6,
	    245,  10,  96,
	    233,  89,  17,
	    229, 103, 101,
	    154,  39, 161,
	    163,  26, 249,
	    158,  77, 159,
	    149,  94, 254,
	    234,  20, 160,
	    234,  29, 242,
	    233,  76, 163,
	    218,  81, 244,
	    163, 151,  10,
	    157, 156, 102,
	    164, 214,  45,
	    165, 242,  87,
	    223, 174,  17,
	    228, 160,  77,
	    242, 232,  15,
	    233, 218, 102,
	    138, 138, 138,
	    142, 141, 176,
	    148, 180, 180,
	    174, 141, 140,
	    169, 130, 168,
	    181, 179, 136,
	    176, 176, 177,
	    172, 170, 220,
	    133, 207, 177,
	    171, 236, 233,
	    231, 169, 157,
	    252, 170, 253,
	    247, 243, 168,
	    202, 204, 204,
	    201, 201, 243,
	    208, 238, 238,
	    246, 212, 212,
	    248, 244, 198,
	    250, 250, 250
    };

    struct
    {
	    LOGPALETTE lp;
	    PALETTEENTRY ape[NUMPALCOLORS-1];
    } pal;

    LOGPALETTE* pLP = (LOGPALETTE*)&pal;
    pLP->palVersion = 0x300;
    pLP->palNumEntries = NUMPALCOLORS;

    for (int i = 0; i < pLP->palNumEntries; i++)
    {
	    pLP->palPalEntry[i].peRed = byVals[i][0];
	    pLP->palPalEntry[i].peGreen = byVals[i][1];
	    pLP->palPalEntry[i].peBlue = byVals[i][2];
	    pLP->palPalEntry[i].peFlags = 0;
    }

    return (CreatePalette(pLP));
}

////////////////////////////////////////////////////////////////////////////////////////////
// * GetCurrentCDDrive
// returns the drive number of the cd that is currently selected in the cdplayer ui
////////////////////////////////////////////////////////////////////////////////////////////
int GetCurrentCDDrive()
{
	IMMComponentAutomation* pAuto = NULL;
	HRESULT hr = pNodeCurrent->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&pAuto);
	if ((SUCCEEDED(hr)) && (pAuto != NULL))
	{
        MMMEDIAID mmMedia;
        mmMedia.nDrive = -1;
        pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);
        pAuto->Release();
        return (mmMedia.nDrive);
    }

    return -1;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * InitCDVol
// Sets up the mixer structures for the current cd drive
////////////////////////////////////////////////////////////////////////////////////////////
BOOL InitCDVol(HWND hwndCallback, LPCDOPTIONS pCDOpts)
{
    //figure out which drive we're on
    int nDrive = GetCurrentCDDrive();

    //return if the drive is bogus
    if (nDrive < 0)
    {
        return FALSE;
    }

    //get the cdunit info from the options
    CDUNIT* pCDUnit = pCDOpts->pCDUnitList;
    
    //scan the list to find the one we want
    for (int index = 0; index < nDrive; index++)
    {
        pCDUnit = pCDUnit->pNext;
    }
    
    //check to see if we already have an open mixer
    if (hmix!=NULL)
    {
        //we've been here before ... may not need to be here now,
        //if both the mixer id and the control id are the same
        if ((dwLastMixID == pCDUnit->dwMixID) &&
            (mixerlinedetails.dwControlID == pCDUnit->dwVolID))
        {
            return FALSE;
        }

        //a change is coming, go ahead and close this mixer
        mixerClose((HMIXER)hmix);
    }

    //remember our last mixer id
    dwLastMixID = pCDUnit->dwMixID;

    //open the mixer
    mixerOpen((HMIXER*)(&hmix),pCDUnit->dwMixID,(DWORD_PTR)hwndCallback,0,CALLBACK_WINDOW|MIXER_OBJECTF_MIXER);
	
	Volume_DeviceChange_Init(hwndCallback, pCDUnit->dwMixID);
	
	MIXERLINE           mlDst;
	MMRESULT            mmr;
	int					newDest;
    
    ZeroMemory(&mlDst, sizeof(mlDst));
    
    mlDst.cbStruct      = sizeof(mlDst);
    mlDst.dwDestination = pCDUnit->dwDestID;
    
    mmr = mixerGetLineInfo((HMIXEROBJ)hmix
                           , &mlDst
                           , MIXER_GETLINEINFOF_DESTINATION);

    //save the details of the volume line
    mixerlinedetails.cbStruct = sizeof(mixerlinedetails);
    mixerlinedetails.dwControlID = pCDUnit->dwVolID;
    mixerlinedetails.cChannels = mlDst.cChannels;
    mixerlinedetails.hwndOwner = 0;
    mixerlinedetails.cMultipleItems = 0;
    mixerlinedetails.cbDetails = sizeof(DWORD); //seems like it would be sizeof(mixervalue),
                                                //but actually, it is the size of a single value
                                                //and is multiplied by channel in the driver.
    mixerlinedetails.paDetails = &mixervalue[0];
                    
    //save the details of the mute line
    mutelinedetails.cbStruct = sizeof(mutelinedetails);
    mutelinedetails.dwControlID = pCDUnit->dwMuteID;
    mutelinedetails.cChannels = 1;
    mutelinedetails.hwndOwner = 0;
    mutelinedetails.cMultipleItems = 0;
    mutelinedetails.cbDetails = sizeof(fmutevalue);
    mutelinedetails.paDetails = &fmutevalue;

    //save the name of the volume line
    _tcscpy(szLineName,pCDUnit->szVolName);

    return TRUE;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * GetVolume
////////////////////////////////////////////////////////////////////////////////////////////
DWORD GetVolume()
{
    //get the value of this mixer control line
    ZeroMemory(mixervalue,sizeof(DWORD)*2);
    mixerGetControlDetails(hmix,&mixerlinedetails,MIXER_GETCONTROLDETAILSF_VALUE);
    return ((mixervalue[0] > mixervalue[1]) ? mixervalue[0] : mixervalue[1]);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * SetVolume
////////////////////////////////////////////////////////////////////////////////////////////
void SetVolume(DWORD dwVol)
{
    LONG        lBalance = 0;

    //if this is a stereo device, we need to check the balance
    if (mixerlinedetails.cChannels > 1)
    {
        ZeroMemory(mixervalue,sizeof(DWORD)*2);
        mixerGetControlDetails(hmix,&mixerlinedetails,MIXER_GETCONTROLDETAILSF_VALUE);

        LONG lDiv =  (LONG)(max(mixervalue[0], mixervalue[1]) - 0);

        //
        // if we're pegged, don't try to calculate the balance.
        //
        if (mixervalue[0] == 0 && mixervalue[1] == 0)
            lBalance = lCachedBalance;
        else if (mixervalue[0] == 0)
            lBalance = 32;
        else if (mixervalue[1] == 0) 
            lBalance = -32;
        else if (lDiv > 0)
        {
            lBalance = (32 * ((LONG)mixervalue[1]-(LONG)mixervalue[0]))
                       / lDiv;
            //
            // we always lose precision doing this.
            //
            if (lBalance > 0) lBalance++;
            if (lBalance < 0) lBalance--;

            //if we lost precision above, we can get it back by checking
            //the previous value of our balance.  We're usually only off by
            //one if this is the result of a rounding error.  Otherwise,
            //we probably have a different balance because the user set it.
            if (((lCachedBalance - lBalance) == 1) ||
                ((lCachedBalance - lBalance) == -1))
            {
                lBalance = lCachedBalance;
            }
        
        }
        else
            lBalance = 0;
    }

    //save this balance setting so we can use it if we're pegged later
    lCachedBalance = lBalance;

    //
    // Recalc channels based on Balance vs. Volume
    //
    mixervalue[0] = dwVol;
    mixervalue[1] = dwVol;
                   
    if (lBalance > 0)
        mixervalue[0] -= (lBalance * (LONG)(mixervalue[1]-0))
                        / 32;
    else if (lBalance < 0)
        mixervalue[1] -= (-lBalance * (LONG)(mixervalue[0]-0))
                        / 32;

    mixerSetControlDetails(hmix,&mixerlinedetails,MIXER_SETCONTROLDETAILSF_VALUE);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * GetMute
////////////////////////////////////////////////////////////////////////////////////////////
BOOL GetMute()
{
    if (mutelinedetails.dwControlID != DWORD(-1))
    {
        mixerGetControlDetails(hmix,&mutelinedetails,MIXER_GETCONTROLDETAILSF_VALUE);
    }
    else
    {
        //mixer line doesn't exist, assume not muted
        fmutevalue = FALSE;
    }

    return (fmutevalue);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * SetMute
// Implemented as a toggle from current state
////////////////////////////////////////////////////////////////////////////////////////////
void SetMute()
{
    if (mutelinedetails.dwControlID != DWORD(-1))
    {
        if (GetMute())
        {
            //muted, so unmute
            fmutevalue = FALSE;
            mixerSetControlDetails(hmix,&mutelinedetails,MIXER_SETCONTROLDETAILSF_VALUE);
        }
        else
        {
            //not muted, so mute
            fmutevalue = TRUE;
            MMRESULT mmr = mixerSetControlDetails(hmix,&mutelinedetails,MIXER_SETCONTROLDETAILSF_VALUE);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * CanStartShell()
// Checks to see if we can launch if wanting to do so in shell mode
// Returns FALSE only if asking for "tray mode" and if reg setting is FALSE (or not present)
////////////////////////////////////////////////////////////////////////////////////////////
BOOL CanStartShell()
{
    BOOL retval = TRUE; //default to allowing launch
    fShellMode = FALSE;

    //if asking for permission to launch try, see if the registry setting is there
    HKEY hKeySettings;
	long lResult = ::RegOpenKeyEx( HKEY_CURRENT_USER,
							  REG_KEY_SHELLSETTINGS,
							  0, KEY_READ, &hKeySettings );

    if (lResult == ERROR_SUCCESS)
    {
        DWORD fEnable = FALSE;
        DWORD dwType = REG_DWORD;
        DWORD dwCbData = sizeof(fEnable);
        lResult  = ::RegQueryValueEx( hKeySettings, REG_KEY_SHELLENABLE, NULL,
						          &dwType, (LPBYTE)&fEnable, &dwCbData );

        if (fEnable)
        {
            fShellMode = TRUE;
        }

        RegCloseKey(hKeySettings);

        //check for the query on the command line
        TCHAR szCommand[MAX_PATH];
    
        _tcscpy(szCommand,GetCommandLine());
        _tcslwr(szCommand);
        if ((_tcsstr(szCommand,TRAYCOMMAND1) != NULL)
            ||
            (_tcsstr(szCommand,TRAYCOMMAND2) != NULL))
        {
            //user wants to check try status ... base on fenable
            retval = (BOOL)fEnable;
        }
    } //end if regkey

    return (retval);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * ShellOnly()
// Returns TRUE if we should not make the main UI visible.
////////////////////////////////////////////////////////////////////////////////////////////
BOOL ShellOnly()
{
    BOOL retval = FALSE;

    if (fShellMode)
    {
        //check for the query on the command line
        TCHAR szCommand[MAX_PATH];
    
        _tcscpy(szCommand,GetCommandLine());
        _tcslwr(szCommand);
        if ((_tcsstr(szCommand,TRAYCOMMAND1) != NULL)
            ||
            (_tcsstr(szCommand,TRAYCOMMAND2) != NULL))
        {
            retval = TRUE;
        }
    }

    return (retval);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * IsOnlyInstance 
// Check to see if this is the only instance, based on the webcd mutex
////////////////////////////////////////////////////////////////////////////////////////////
BOOL IsOnlyInstance()
{
    hMutex = CreateMutex(NULL,TRUE,WEBCD_MUTEX);
    if (GetLastError()==ERROR_ALREADY_EXISTS)
    {
        if (hMutex!=NULL)
        {
            ReleaseMutex(hMutex);
            CloseHandle(hMutex);
            hMutex = NULL;
        }

        //send the command line to the app that is already running
        HWND hwndFind = FindWindow(FRAMEWORK_CLASS, NULL);

        if (hwndFind)
        {
            //we only want to do this if NOT "autoplayed" ... that is, if /play is on the
            //command line, don't refocus us ... see bug 1244
            //(the old cdplayer implements it this way, too!)
            TCHAR szCommand[MAX_PATH];
            
            _tcscpy(szCommand,GetCommandLine());
            _tcslwr(szCommand);
            if ((_tcsstr(szCommand,PLAYCOMMAND1) == NULL)
                &&
                (_tcsstr(szCommand,PLAYCOMMAND2) == NULL))
            {
                //get the most recent "child" window
                hwndFind = GetLastActivePopup(hwndFind);

                //bring the window up if it is iconic
                if (IsIconic(hwndFind))
                {
                    ShowWindow(hwndFind,SW_RESTORE);
                }

                //display the window
                ShowWindow(hwndFind,SW_SHOW); //this "wakes up" if in shell mode in other inst.
		        BringWindowToTop(hwndFind);
		        SetForegroundWindow(hwndFind);
            }

            //forward the command line found to the second instance,
            //only if it is NOT an "autoplay" message -- we'll  scan that instead
            TCHAR tempCmdLine[MAX_PATH];
            _tcscpy(tempCmdLine,GetCommandLine());
            if (_tcslen(tempCmdLine) > 0)
            {
                if (tempCmdLine[_tcslen(tempCmdLine)-1] != TEXT('\\'))
                {
                    COPYDATASTRUCT  cpds;
	                cpds.dwData = 0L;
	                cpds.cbData = (_tcslen(GetCommandLine()) + 1) * sizeof(TCHAR);
	                cpds.lpData = LocalAlloc(LPTR,cpds.cbData);
	                if (cpds.lpData == NULL) {
	                    // Error - not enough memory to continue
	                    return (FALSE);
	                }

	                _tcscpy((LPTSTR)cpds.lpData, GetCommandLine());

	                SendMessage(hwndFind, WM_COPYDATA, 0, (LPARAM)(LPVOID)&cpds);
	                LocalFree((HLOCAL)cpds.lpData);
                } //end if not autoplay command line
            } //end if non-0 command line

        } //end if found other window

        return (FALSE);
    }

    return (TRUE);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * CalculateDispAreaOffset 
// Figures out how big the display area should be if we're not in standard font mode
////////////////////////////////////////////////////////////////////////////////////////////
void CalculateDispAreaOffset(IMMComponent* pComp)
{
    if (!pComp)
    {
        return;
    }
    
    MMCOMPDATA mmComp;
    mmComp.dwSize = sizeof(mmComp);
    pComp->GetInfo(&mmComp);

    //mmComp.rect (height) contains the min height of the display area on this monitor
    //for the largest view ... other views seem to be OK with different font settings

    //calculate how big the view must be compared to its normal min size
     nDispAreaOffset = (mmComp.rect.bottom - mmComp.rect.top) - TYPICAL_DISPLAY_AREA;

     //don't let the display area shrink, only grow
     if (nDispAreaOffset < 0)
     {
        nDispAreaOffset = 0;
     }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * BuildFrameworkBitmaps 
// Creates the bitmaps for normal, restored, and small sizes
////////////////////////////////////////////////////////////////////////////////////////////
BOOL BuildFrameworkBitmaps()
{
    POINT ptSys = {SYSMENU_XOFFSET,TITLEBAR_YOFFSET_LARGE};
    RECT rectMain = {0,0,480,150};
    RECT rectView = {10,25,472,98};
    RECT rectSeps[2] = {93,97,95,146,302,97,304,146};
    
    rectView.bottom += nDispAreaOffset;
    rectMain.bottom += nDispAreaOffset;

    for (UINT i = 0; i < sizeof(rectSeps) / sizeof(RECT); i++)
    {
        rectSeps[i].top += nDispAreaOffset;
        rectSeps[i].bottom += nDispAreaOffset;
    }

    HDC hdcMain = GetDC(hwndMain);
    hbmpMain = BuildFrameBitmap(hdcMain,&rectMain,&rectView,VIEW_MODE_NORMAL,&ptSys,rectSeps,2,&bmMain);

    //"no title bar" mode
    ptSys.x = SYSMENU_XOFFSET;
    ptSys.y = TITLEBAR_YOFFSET_LARGE;
    SetRect(&rectMain,0,0,480,134);
    SetRect(&rectView,10,9,472,82);
    SetRect(&rectSeps[0],93,81,95,130);
    SetRect(&rectSeps[1],302,81,304,130);
    hbmpMainNoBar = BuildFrameBitmap(hdcMain,&rectMain,&rectView,VIEW_MODE_NOBAR,&ptSys,rectSeps,2,&bmMainNoBar);

    //"restored" mode
    ptSys.x = SYSMENU_XOFFSET;
    ptSys.y = TITLEBAR_YOFFSET_SMALL;
    SetRect(&rectMain,0,0,393,50);
    SetRect(&rectView,301,21,386,43);
    SetRect(&rectSeps[0],92,25,101,38);
    SetRect(&rectSeps[1],211,25,220,38);
    hbmpMainRestore = BuildFrameBitmap(hdcMain,&rectMain,&rectView,VIEW_MODE_RESTORE,&ptSys,rectSeps,2,&bmMainRestore);

    //"very small" mode, no title bar
    SetRect(&rectMain,0,0,393,38);
    SetRect(&rectView,301,9,386,30);
    SetRect(&rectSeps[0],92,13,101,26);
    SetRect(&rectSeps[1],211,13,220,26);
    hbmpMainSmall = BuildFrameBitmap(hdcMain,&rectMain,&rectView,VIEW_MODE_SMALL,&ptSys,rectSeps,2,&bmMainSmall);

    ReleaseDC(hwndMain,hdcMain);

    return TRUE;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * SetCurvedEdges
// Changes clipping region of an HWND to have curved corners
////////////////////////////////////////////////////////////////////////////////////////////
void SetCurvedEdges(HWND hwnd)
{
    RECT rect;
    GetWindowRect(hwnd,&rect);

    //set the rect to "client" coordinates
    rect.bottom = rect.bottom - rect.top;
    rect.right = rect.right - rect.left;
    rect.top = 0;
    rect.left = 0;
    
    HRGN region = CreateRoundRectRgn(GetXOffset(),
                                     GetYOffsetCaption() + GetYOffset(),
                                     (rect.right - GetXOffset())+1,
                                     (rect.bottom - GetYOffset())+1,
                                     EDGE_CURVE_WIDTH,
                                     EDGE_CURVE_HEIGHT);

    SetWindowRgn(hwnd,region,TRUE);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * SetNoBarMode(HWND hwnd)
// Changes the view mode to have no title bar
////////////////////////////////////////////////////////////////////////////////////////////
void SetNoBarMode(HWND hwnd)
{
    g_nViewMode = VIEW_MODE_NOBAR;

    HDWP hdwp = BeginDeferWindowPos(NUM_BUTTONS+3);

    //move/size/hide buttons
    for (int i = 0; i < NUM_BUTTONS; i++)
    {
		hdwp = DeferWindowPos(hdwp,GetDlgItem(hwnd,biButtons[i].id),hwnd,
			 biButtons[i].uixy.x,
			 biButtons[i].uixy.y - (bmMain.bmHeight - bmMainNoBar.bmHeight),
			 biButtons[i].width,
			 biButtons[i].height,
			 SWP_NOACTIVATE|SWP_NOZORDER);

        if (biButtons[i].dwStyle == MBS_SYSTEMTYPE)
        {
            ShowWindow(GetDlgItem(hwnd,biButtons[i].id),SW_HIDE); 
        }
    }

    //move volume and mute
	hdwp = DeferWindowPos(hdwp,GetDlgItem(hwnd,IDB_VOLUME),hwnd,
		 403,
		 (93+nDispAreaOffset) - (bmMain.bmHeight - bmMainNoBar.bmHeight),
		 45,
		 45,
		 SWP_NOACTIVATE|SWP_NOZORDER);

	hdwp = DeferWindowPos(hdwp,GetDlgItem(hwnd,IDB_MUTE),hwnd,
		 450,
		 (122+nDispAreaOffset) - (bmMain.bmHeight - bmMainNoBar.bmHeight),
		 13,
		 13,
		 SWP_NOACTIVATE|SWP_NOZORDER);

    //move display screen
    hdwp = DeferWindowPos(hdwp,hwndCurrentComp,hwnd,24,32-(bmMain.bmHeight - bmMainNoBar.bmHeight),431,56+nDispAreaOffset,SWP_NOACTIVATE|SWP_NOZORDER);
    InvalidateRect(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),NULL,TRUE);

    //size main window
    int sx = GetXOffset()*2;
    int sy = (GetYOffset()*2) + GetYOffsetCaption();
    SetWindowPos(hwnd,NULL,0,0,
	     bmMainNoBar.bmWidth+sx,
	     bmMainNoBar.bmHeight+sy,
	     SWP_NOMOVE|SWP_NOZORDER);

    SetCurvedEdges(hwnd);

    InvalidateRect(hwnd,NULL,TRUE);
    EndDeferWindowPos(hdwp);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * SetRestoredMode
// Changes the view mode to restored
////////////////////////////////////////////////////////////////////////////////////////////
void SetRestoredMode(HWND hwnd)
{
    if (g_nViewMode == VIEW_MODE_NORMAL)
    {
        //pre-blit the new button sizes
        CMButton* pButton;
        pButton = GetMButtonFromID(hwnd,IDB_PLAY);
        pButton->PreDrawUpstate(biButtons[2].width2,biButtons[2].height);
        pButton = GetMButtonFromID(hwnd,IDB_STOP);
        pButton->PreDrawUpstate(biButtons[3].width2,biButtons[3].height);
        pButton = GetMButtonFromID(hwnd,IDB_EJECT);
        pButton->PreDrawUpstate(biButtons[4].width2,biButtons[4].height);
        pButton = GetMButtonFromID(hwnd,IDB_TRACK);
        pButton->PreDrawUpstate(biButtons[10].width2,biButtons[10].height);
    }

    g_nViewMode = VIEW_MODE_RESTORE;

    HDWP hdwp = BeginDeferWindowPos(NUM_BUTTONS+2);

    //move/size/hide buttons
    for (int i = 0; i < NUM_BUTTONS; i++)
    {
	    if (biButtons[i].uixy2.x != 0)
	    {
		    hdwp = DeferWindowPos(hdwp,GetDlgItem(hwnd,biButtons[i].id),hwnd,
			     biButtons[i].uixy2.x,
			     biButtons[i].uixy2.y,
			     biButtons[i].width2,
			     biButtons[i].height,
			     SWP_NOACTIVATE|SWP_NOZORDER);
	    }
        else
        {
            ShowWindow(GetDlgItem(hwnd,biButtons[i].id),SW_HIDE); //prevents tabbing
        }

        if (biButtons[i].dwStyle == MBS_SYSTEMTYPE)
        {
            ShowWindow(GetDlgItem(hwnd,biButtons[i].id),SW_SHOW); 
        }
    }

    //move display screen
    hdwp = DeferWindowPos(hdwp,hwndCurrentComp,hwnd,303,24,81,17,SWP_NOACTIVATE|SWP_NOZORDER);
    InvalidateRect(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),NULL,TRUE);

    //size main window
    int sx = GetXOffset()*2;
    int sy = (GetYOffset()*2) + GetYOffsetCaption();
    SetWindowPos(hwnd,NULL,0,0,
	     bmMainRestore.bmWidth+sx,
	     bmMainRestore.bmHeight+sy,
	     SWP_NOMOVE|SWP_NOZORDER);

    ShowWindow(GetDlgItem(hwnd,IDB_VOLUME),SW_HIDE);
    ShowWindow(GetDlgItem(hwnd,IDB_MUTE),SW_HIDE);
    ShowWindow(GetDlgItem(hwnd,IDB_SET_NORMAL_MODE),SW_SHOW);
    ShowWindow(GetDlgItem(hwnd,IDB_SET_TINY_MODE),SW_HIDE);

    SetCurvedEdges(hwnd);

    InvalidateRect(hwnd,NULL,TRUE);
    EndDeferWindowPos(hdwp);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * SetNormalMode
// Changes the view mode to normal
////////////////////////////////////////////////////////////////////////////////////////////
void SetNormalMode(HWND hwnd)
{
    //going from restore to max
    g_nViewMode = VIEW_MODE_NORMAL;

    //pre-blit the new button sizes
    CMButton* pButton;
    pButton = GetMButtonFromID(hwnd,IDB_PLAY);
    pButton->PreDrawUpstate(biButtons[2].width,biButtons[2].height);
    pButton = GetMButtonFromID(hwnd,IDB_STOP);
    pButton->PreDrawUpstate(biButtons[3].width,biButtons[3].height);
    pButton = GetMButtonFromID(hwnd,IDB_EJECT);
    pButton->PreDrawUpstate(biButtons[4].width,biButtons[4].height);
    pButton = GetMButtonFromID(hwnd,IDB_TRACK);
    pButton->PreDrawUpstate(biButtons[10].width,biButtons[10].height);

    HDWP hdwp = BeginDeferWindowPos(NUM_BUTTONS+3);

    //move/size/show buttons
    for (int i = 0; i < NUM_BUTTONS; i++)
    {
        ShowWindow(GetDlgItem(hwnd,biButtons[i].id),SW_SHOW);
        hdwp = DeferWindowPos(hdwp,GetDlgItem(hwnd,biButtons[i].id),hwnd,
			          biButtons[i].uixy.x,
			          biButtons[i].uixy.y,
			          biButtons[i].width,
			          biButtons[i].height,
			          SWP_NOACTIVATE|SWP_NOZORDER);
    }

    //move volume and mute
	hdwp = DeferWindowPos(hdwp,GetDlgItem(hwnd,IDB_VOLUME),hwnd,
		 403,
		 93+nDispAreaOffset,
		 45,
		 45,
		 SWP_NOACTIVATE|SWP_NOZORDER);

	hdwp = DeferWindowPos(hdwp,GetDlgItem(hwnd,IDB_MUTE),hwnd,
		 450,
		 122+nDispAreaOffset,
		 13,
		 13,
		 SWP_NOACTIVATE|SWP_NOZORDER);

    //move display screen
    hdwp = DeferWindowPos(hdwp,hwndCurrentComp,hwnd,24,32,431,56+nDispAreaOffset,SWP_NOACTIVATE|SWP_NOZORDER);
    InvalidateRect(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),NULL,TRUE);

    ShowWindow(GetDlgItem(hwnd,IDB_VOLUME),SW_SHOW);
    ShowWindow(GetDlgItem(hwnd,IDB_MUTE),SW_SHOW);
    ShowWindow(GetDlgItem(hwnd,IDB_SET_NORMAL_MODE),SW_HIDE);
    ShowWindow(GetDlgItem(hwnd,IDB_SET_TINY_MODE),SW_SHOW);

    //Resize window
    int sx = GetXOffset()*2;
    int sy = (GetYOffset()*2) + GetYOffsetCaption();
    SetWindowPos(hwnd,NULL,0,0,
	     bmMain.bmWidth+sx,
	     bmMain.bmHeight+sy,
	     SWP_NOMOVE|SWP_NOZORDER);

    SetCurvedEdges(hwnd);

    InvalidateRect(hwnd,NULL,TRUE);
    EndDeferWindowPos(hdwp);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * SetSmallMode
// Changes the view mode to small
////////////////////////////////////////////////////////////////////////////////////////////
void SetSmallMode(HWND hwnd)
{
    g_nViewMode = VIEW_MODE_SMALL;

    HDWP hdwp = BeginDeferWindowPos(NUM_BUTTONS+2);

    //move/size/hide buttons
    for (int i = 0; i < NUM_BUTTONS; i++)
    {
	    if (biButtons[i].uixy2.x != 0)
	    {
		    hdwp = DeferWindowPos(hdwp,GetDlgItem(hwnd,biButtons[i].id),hwnd,
			     biButtons[i].uixy2.x,
			     biButtons[i].uixy2.y - (bmMainRestore.bmHeight - bmMainSmall.bmHeight),
			     biButtons[i].width2,
			     biButtons[i].height,
			     SWP_NOACTIVATE|SWP_NOZORDER);
	    }

        if (biButtons[i].dwStyle == MBS_SYSTEMTYPE)
        {
            ShowWindow(GetDlgItem(hwnd,biButtons[i].id),SW_HIDE); 
        }
    }

    //move display screen
    hdwp = DeferWindowPos(hdwp,hwndCurrentComp,hwnd,303,24-(bmMainRestore.bmHeight - bmMainSmall.bmHeight),81,17,SWP_NOACTIVATE|SWP_NOZORDER);
    InvalidateRect(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),NULL,TRUE);

    //size main window
    int sx = GetXOffset()*2;
    int sy = (GetYOffset()*2) + GetYOffsetCaption();
    SetWindowPos(hwnd,NULL,0,0,
	     bmMainSmall.bmWidth+sx,
	     bmMainSmall.bmHeight+sy,
	     SWP_NOMOVE|SWP_NOZORDER);

    SetCurvedEdges(hwnd);

    InvalidateRect(hwnd,NULL,TRUE);
    EndDeferWindowPos(hdwp);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * AdjustForMultimon
// Will move the app onto the primary display if its x,y settings are not on a monitor
////////////////////////////////////////////////////////////////////////////////////////////
void AdjustForMultimon(HWND hwnd)
{
    RECT rect;
    GetWindowRect(hwnd,&rect);

    int cxWnd = rect.right - rect.left;
    int cyWnd = rect.bottom - rect.top;

	// Check if the app's rect is visible is any of the monitors
	if( NULL == MonitorFromRect( &rect, 0L ) )
	{
		//The window is not visible. Let's center it in the primary monitor.
		//Note: the window could be in this state if (1) the display mode was changed from 
		//a high-resolution to a lower resolution, with the cdplayer in the corner. Or,
		//(2) the multi-mon configuration was rearranged.

		RECT rcDesktop;

        GetWindowRect( GetDesktopWindow(), &rcDesktop );
		int cxDesktop = (rcDesktop.right - rcDesktop.left);
		int cyDesktop = (rcDesktop.bottom - rcDesktop.top);

		int x = (cxDesktop - cxWnd) / 2; //center in x
		int y = (cyDesktop - cyWnd) / 3; //and a little towards the top

        SetWindowPos(hwnd,NULL,x,y,0,0,SWP_NOSIZE|SWP_NOZORDER);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////
// * WinMain
// Entry point for application
////////////////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstEXE, HINSTANCE hInstEXEPrev, PSTR lpszCmdLine, int nCmdShow)
{
    //first thing, must check tray icon state
    if (!CanStartShell())
    {
        return (0);
    }
        
    if (!IsOnlyInstance())
    {
        //can't have more than one of these
        return (0);
    }
    
    //save the global hinstance
    hInst = hInstEXE;

    DetermineColorMode();
    
    //start our linked list of components
    pCompList = new COMPNODE;
    ZeroMemory(pCompList,sizeof(COMPNODE));
    pCompListTail = pCompList;

    //load the app name
    LoadString(hInstEXE,IDS_APPNAME,szAppName,sizeof(szAppName)/sizeof(TCHAR));

    //init the networking component (this just inits some crit sections)
    CDNET_Init(hInstEXE);

    //load components from registry
    if (!LoadComponents())
    {
	    CleanUp();
	    return (0);
    }
    
    //register our main window class  
    WNDCLASSEX wc;
    ATOM atomClassName;
    ZeroMemory(&wc, sizeof(wc));
    wc.cbSize = sizeof(wc);
    wc.lpszClassName = FRAMEWORK_CLASS;
    wc.lpfnWndProc = MainWndProc;
    wc.hInstance = hInstEXE;
    wc.style = CS_DBLCLKS;
    wc.hIcon = LoadIcon(hInstEXE, MAKEINTRESOURCE(IDI_MMFW));
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName = NULL;
    wc.hIconSm = (HICON)LoadImage(hInstEXE, MAKEINTRESOURCE(IDI_MMFW), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
    
    atomClassName = RegisterClassEx(&wc);

    int x = CW_USEDEFAULT;
    int y = CW_USEDEFAULT;
    
    GetSettings(x,y);

	hpalMain = SetPalette();

    #ifndef MMFW_USE_CAPTION
    DWORD dwStyle = WS_POPUP|WS_SYSMENU|WS_MINIMIZEBOX|WS_CLIPCHILDREN;
    #else
    DWORD dwStyle = WS_POPUP|WS_SYSMENU|WS_MINIMIZEBOX|WS_CAPTION|WS_CLIPCHILDREN;
    #endif

    //create our main window
    HWND hwnd = CreateWindowEx(WS_EX_APPWINDOW,
			     MAKEINTATOM(atomClassName),
			     szAppName,
			     dwStyle,
			     x,
			     y,
			     0,
			     0,
			     NULL,
			     NULL,
			     hInstEXE,
			     NULL);

    if (hwnd == NULL)
    {
        //major failure here!
        CleanUp();
        return 0;
    }

    hwndMain = hwnd;

    //tell our sink what our main window is
    CFrameworkNotifySink::m_hwndTitle = hwnd;

    //create the bitmaps of the main ui
    if (!BuildFrameworkBitmaps())
    {
        //failure -- can't create bitmaps for framework
        CleanUp();
        return 0;
    }

    int bmWidth = bmMain.bmWidth;
    int bmHeight = bmMain.bmHeight;

    //set window size to match the width and height of the correct mode's bitmap
    switch (g_nViewMode)
    {
        case VIEW_MODE_RESTORE :
        {
            bmWidth = bmMainRestore.bmWidth;
            bmHeight = bmMainRestore.bmHeight;
        }
        break;

        case VIEW_MODE_SMALL :
        {
            bmWidth = bmMainSmall.bmWidth;
            bmHeight = bmMainSmall.bmHeight;
        }
        break;

        case VIEW_MODE_NOBAR :
        {
            bmWidth = bmMainNoBar.bmWidth;
            bmHeight = bmMainNoBar.bmHeight;
        }
        break;
    }

    int sx = GetXOffset()*2;
    int sy = (GetYOffset()*2) + GetYOffsetCaption();
    SetWindowPos(hwnd,NULL,0,0,bmWidth+sx,bmHeight+sy,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);

    //check the window pos against multimon
    AdjustForMultimon(hwnd);

    SetCurvedEdges(hwnd);

    //Send us a message to set the initial mode of the player
    SendMessage(hwnd,WM_COMMAND,MAKEWPARAM(nCDMode,0),0);

    //show us!
    if (!ShellOnly())
    {
        ShowWindow(hwnd, nCmdShow);
        UpdateWindow(hwnd);

        LPCDDATA pCDData = GetCDData();

        if (pCDData)
        {
            pCDData->Initialize(hwnd);
            pCDData->CheckDatabase(hwnd);
        }
    }

    if (fShellMode)
    {
        CreateShellIcon(hInst,hwnd,pNodeCurrent,szAppName);
    }

    //main message loop
    MSG msg;

    for (;;)
    {
        if (PeekMessage(&msg, NULL, 0, 0,PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

	        /*
            if (hAccelApp && TranslateAccelerator(hwndApp, hAccelApp, &msg))
		    continue;
            */

	        if (!IsDialogMessage(hwnd,&msg))
	        {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            } //end if dialg msg
        }
        else
	    {
            WaitMessage();
        }
    } //end for

    //get outta here!
    CleanUp();

    return ((int)msg.wParam);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * ShowNewComponentWindow
// Displays the chosen component
// (Sort of a holdover from the old multi-component days, but it also serves to
// initialize the first component loaded)
////////////////////////////////////////////////////////////////////////////////////////////
void ShowNewComponentWindow(PCOMPNODE pNode, HWND hwnd)
{
    if (pNode == NULL)
    {
    	return;
    }

    //don't bother if we're already there
    if (pNode->hwndComp == hwndCurrentComp)
    {
	    return;
    }
    
    if (hwndCurrentComp != NULL)
    {
	    ShowWindow(hwndCurrentComp,SW_HIDE);
    }

    hwndCurrentComp = pNode->hwndComp;
    
    ShowWindow(hwndCurrentComp,SW_SHOW);

    pNodeCurrent = pNode;

    MMCOMPDATA mmComp;
    mmComp.dwSize = sizeof(mmComp);
    pNode->pComp->GetInfo(&mmComp);

    if (_tcslen(pNode->szTitle)==0)
    {
	    _tcscpy(pNode->szTitle,mmComp.szName);
    }

    SetWindowText(hwnd,szAppName);

    //also set icons
    if (mmComp.hiconLarge != NULL)
    {
    	SendMessage(hwnd, WM_SETICON, TRUE, (LPARAM)mmComp.hiconLarge);
    }

    if (mmComp.hiconSmall != NULL)
    {
	    SendMessage(hwnd, WM_SETICON, FALSE, (LPARAM)mmComp.hiconSmall);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * InitButtonProperties
// Set up the button info structure for all the transport buttons
//
// bugbug: this is pretty ugly and hard-coded.  Implement to read from some kind of
//         easily-editable resource for when layout changes come?
////////////////////////////////////////////////////////////////////////////////////////////
void InitButtonProperties()
{
    //set up each button's properties
    ZeroMemory(biButtons,sizeof(BUTTONINFO)*NUM_BUTTONS);

    //order of buttons in this array affects the tab order
    biButtons[0].id = IDB_OPTIONS;
    biButtons[0].nToolTipID = IDB_TT_OPTIONS;
    biButtons[0].uixy.x = 11;
    biButtons[0].uixy.y = 102 + nDispAreaOffset;
    biButtons[0].width = 76;
    biButtons[0].height = 19;
    biButtons[0].uixy2.x = 9;
    biButtons[0].uixy2.y = 23;
    biButtons[0].width2 = 76;
    biButtons[0].dwStyle = MBS_STANDARDLEFT | MBS_DROPRIGHT;

    biButtons[1].id = IDB_NET;
    biButtons[1].nToolTipID = IDB_TT_NET;
    biButtons[1].uixy.x = 11;
    biButtons[1].uixy.y = 125 + nDispAreaOffset;
    biButtons[1].width = 76;
    biButtons[1].height = 19;
    biButtons[1].dwStyle = MBS_STANDARDLEFT | MBS_DROPRIGHT;

    biButtons[2].id = IDB_PLAY;
    biButtons[2].nToolTipID = IDB_TT_PLAY;
    biButtons[2].uixy.x = 103;
    biButtons[2].uixy.y = 102 + nDispAreaOffset;
    biButtons[2].width = 64;
    biButtons[2].height = 19; 
    biButtons[2].uixy2.x = 102;
    biButtons[2].uixy2.y = 23;
    biButtons[2].width2 = 34;
    biButtons[2].dwStyle = MBS_STANDARDLEFT | MBS_STANDARDRIGHT;
    biButtons[2].nIconID = IDI_ICON_PLAY;

    biButtons[3].id = IDB_STOP;
    biButtons[3].nToolTipID = IDB_TT_STOP;
    biButtons[3].uixy.x = 174;
    biButtons[3].uixy.y = 102 + nDispAreaOffset;
    biButtons[3].width = 64;
    biButtons[3].height = 19;
    biButtons[3].uixy2.x = 136;
    biButtons[3].uixy2.y = 23;
    biButtons[3].width2 = 34;
    biButtons[3].dwStyle = MBS_STANDARDLEFT | MBS_STANDARDRIGHT;
    biButtons[3].nIconID = IDI_ICON_STOP;

    biButtons[4].id = IDB_EJECT;    
    biButtons[4].nToolTipID = IDB_TT_EJECT;
    biButtons[4].uixy.x = 245;
    biButtons[4].uixy.y = 102 + nDispAreaOffset;
    biButtons[4].width = 51;
    biButtons[4].height = 19;
    biButtons[4].uixy2.x = 170;
    biButtons[4].uixy2.y = 23;
    biButtons[4].width2 = 34;
    biButtons[4].dwStyle = MBS_STANDARDLEFT | MBS_STANDARDRIGHT;
    biButtons[4].nIconID = IDI_ICON_EJECT;

    biButtons[5].id = IDB_REW;
    biButtons[5].nToolTipID = IDB_TT_REW;
    biButtons[5].uixy.x = 103;
    biButtons[5].uixy.y = 125 + nDispAreaOffset;
    biButtons[5].width = 33;
    biButtons[5].height = 19;
    biButtons[5].dwStyle = MBS_STANDARDLEFT | MBS_TOGGLERIGHT;
    biButtons[5].nIconID = IDI_ICON_REW;

    biButtons[6].id = IDB_FFWD;
    biButtons[6].nToolTipID = IDB_TT_FFWD;
    biButtons[6].uixy.x = 136;
    biButtons[6].uixy.y = 125 + nDispAreaOffset;
    biButtons[6].width = 31;
    biButtons[6].height = 19;
    biButtons[6].dwStyle = MBS_TOGGLELEFT | MBS_STANDARDRIGHT;
    biButtons[6].nIconID = IDI_ICON_FFWD;

    biButtons[7].id = IDB_PREVTRACK;
    biButtons[7].nToolTipID = IDB_TT_PREVTRACK;
    biButtons[7].uixy.x = 174;
    biButtons[7].uixy.y = 125 + nDispAreaOffset;
    biButtons[7].width = 33;
    biButtons[7].height = 19;
    biButtons[7].dwStyle = MBS_STANDARDLEFT | MBS_TOGGLERIGHT;
    biButtons[7].nIconID = IDI_ICON_PREV;

    biButtons[8].id = IDB_NEXTTRACK;
    biButtons[8].nToolTipID = IDB_TT_NEXTTRACK;
    biButtons[8].uixy.x = 207;
    biButtons[8].uixy.y = 125 + nDispAreaOffset;
    biButtons[8].width = 31;
    biButtons[8].height = 19;
    biButtons[8].dwStyle = MBS_TOGGLELEFT | MBS_STANDARDRIGHT;
    biButtons[8].nIconID = IDI_ICON_NEXT;

    biButtons[9].id = IDB_MODE;
    biButtons[9].nToolTipID = IDB_TT_MODE;
    biButtons[9].uixy.x = 245;
    biButtons[9].uixy.y = 125 + nDispAreaOffset;
    biButtons[9].width = 51;
    biButtons[9].height = 19;
    biButtons[9].dwStyle = MBS_STANDARDLEFT | MBS_DROPRIGHT;
    biButtons[9].nIconID = IDI_MODE_NORMAL;

    biButtons[10].id = IDB_TRACK;
    biButtons[10].nToolTipID = IDB_TT_TRACK;
    biButtons[10].uixy.x = 312;
    biButtons[10].uixy.y = 102 + nDispAreaOffset;
    biButtons[10].width = 76;
    biButtons[10].height = 19;
    biButtons[10].uixy2.x = 221;
    biButtons[10].uixy2.y = 23;
    biButtons[10].width2 = 72;
    biButtons[10].dwStyle = MBS_STANDARDLEFT | MBS_DROPRIGHT;

    biButtons[11].id = IDB_DISC;
    biButtons[11].nToolTipID = IDB_TT_DISC;
    biButtons[11].uixy.x = 312;
    biButtons[11].uixy.y = 125 + nDispAreaOffset;
    biButtons[11].width = 76;
    biButtons[11].height = 19;
    biButtons[11].dwStyle = MBS_STANDARDLEFT | MBS_DROPRIGHT;

    biButtons[12].id = IDB_CLOSE;
    biButtons[12].nToolTipID = IDB_TT_CLOSE;
    biButtons[12].uixy.x = 456;
    biButtons[12].uixy.y = 7;
    biButtons[12].width = 15;
    biButtons[12].height = 14;
    biButtons[12].fBlockTab = TRUE;
    biButtons[12].uixy2.x = 371;
    biButtons[12].uixy2.y = 4;
    biButtons[12].width2 = 15;
    biButtons[12].dwStyle = MBS_SYSTEMTYPE;
    biButtons[12].nIconID = IDB_CLOSE;

    biButtons[13].id = IDB_MINIMIZE;
    biButtons[13].nToolTipID = IDB_TT_MINIMIZE;
    biButtons[13].uixy.x = 427;
    biButtons[13].uixy.y = 7;
    biButtons[13].width = 14;
    biButtons[13].height =  14;
    biButtons[13].fBlockTab = TRUE;
    biButtons[13].uixy2.x = 343;
    biButtons[13].uixy2.y = 4;
    biButtons[13].width2 = 14;
    biButtons[13].dwStyle = MBS_SYSTEMTYPE;
    biButtons[13].nIconID = IDB_MINIMIZE;

    biButtons[14].id = IDB_SET_TINY_MODE;
    biButtons[14].nToolTipID = IDB_TT_RESTORE;
    biButtons[14].uixy.x = 442;
    biButtons[14].uixy.y = 7;
    biButtons[14].width = 14;
    biButtons[14].height =  14;
    biButtons[14].fBlockTab = TRUE;
    biButtons[14].uixy2.x = 357;
    biButtons[14].uixy2.y = 4;
    biButtons[14].width2 = 14;
    biButtons[14].dwStyle = MBS_SYSTEMTYPE;
    biButtons[14].nIconID = IDB_SET_TINY_MODE;

    biButtons[15].id = IDB_SET_NORMAL_MODE;
    biButtons[15].nToolTipID = IDB_TT_MAXIMIZE;
    biButtons[15].uixy.x = 442;
    biButtons[15].uixy.y = 7;
    biButtons[15].width = 14;
    biButtons[15].height =  14;
    biButtons[15].fBlockTab = TRUE;
    biButtons[15].uixy2.x = 357;
    biButtons[15].uixy2.y = 4;
    biButtons[15].width2 = 14;
    biButtons[15].dwStyle = MBS_SYSTEMTYPE;
    biButtons[15].nIconID = IDB_SET_NORMAL_MODE;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * CreateMuteButton
// Make the little mute button guy
////////////////////////////////////////////////////////////////////////////////////////////
void CreateMuteButton(HWND hwndOwner)
{
	//first load mute button's cursor
    hCursorMute = LoadCursor(hInst,MAKEINTRESOURCE(IDC_MUTE));
    
    CMButton* pButton = NULL;

	TCHAR szCaption[MAX_PATH];
	LoadString(hInst,IDB_MUTE,szCaption,sizeof(szCaption)/sizeof(TCHAR));
	
    int yOffset = 122+nDispAreaOffset;
    if (g_nViewMode == VIEW_MODE_NOBAR)
    {
        yOffset -= 16;
    }

    pButton = CreateMButton(szCaption,IDB_MUTE,WS_VISIBLE|WS_TABSTOP,
				    MBS_SYSTEMTYPE,
				    450,
				    yOffset,
				    13,
				    13,
				    hwndOwner,
				    FALSE,  //create original, not subclass
				    IDB_MUTE,
                    IDB_TT_MUTE,
				    hInst);

    //hide this button in small mode
    if ((g_nViewMode==VIEW_MODE_RESTORE)||((g_nViewMode==VIEW_MODE_SMALL)))
    {
        ShowWindow(pButton->GetHWND(),SW_HIDE); 
    }

    //set up tool tip
    TOOLINFO ti;
    ti.cbSize = sizeof(TOOLINFO); 
    ti.uFlags = TTF_IDISHWND;
    ti.hwnd =  hwndOwner; 
    ti.uId = (UINT_PTR)(pButton->GetHWND());
    ti.hinst = hInst; 
    ti.lpszText = LPSTR_TEXTCALLBACK;
    SendMessage(g_hwndTT, TTM_ADDTOOL, 0,  (LPARAM) (LPTOOLINFO) &ti);

    //make sure button is in correct state if muted on start up
    SendMessage(pButton->GetHWND(),BM_SETSTATE,(WPARAM)GetMute(),0);
    SendMessage(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),WM_LED_MUTE,0,GetMute());
}

////////////////////////////////////////////////////////////////////////////////////////////
// * CreateVolumeKnob
// Put up the volume knob that EVERYONE LOVES
////////////////////////////////////////////////////////////////////////////////////////////
void CreateVolumeKnob(HWND hwndOwner)
{
    LPCDOPT pOpt = GetCDOpt();
    if( pOpt )
    {
        LPCDOPTIONS pCDOpts = pOpt->GetCDOpts();
        InitCDVol(hwndOwner,pCDOpts);

        //ok, this is bad, but I have the options here and the only other thing
        //I need from them is the "topmost" setting for the main window ...
        //so I'll go ahead and take care of that here rather than recreating
        //this struct somewhere or making it global.
        SetWindowPos(hwndOwner,
                     pCDOpts->pCDData->fTopMost ? HWND_TOPMOST : HWND_NOTOPMOST,
                     0,0,0,0,
                     SWP_NOMOVE|SWP_NOSIZE);
    
        DWORD dwVol = GetVolume();
        int yOffset = 93+nDispAreaOffset;
        if (g_nViewMode == VIEW_MODE_NOBAR)
        {
            yOffset -= 16;
        }

        CKnob* pKnob = CreateKnob(WS_VISIBLE | WS_TABSTOP,
	                               0xFFFF,
	                               dwVol,
	                               403,
	                               yOffset,
	                               45,
	                               45,
	                               hwndOwner,
	                               IDB_VOLUME,
	                               hInst);

        TOOLINFO ti;
        ti.cbSize = sizeof(TOOLINFO); 
        ti.uFlags = TTF_IDISHWND;
        ti.hwnd =  hwndOwner; 
        ti.uId = (UINT_PTR)(pKnob->GetHWND());
        ti.hinst = hInst; 
        ti.lpszText = LPSTR_TEXTCALLBACK;
        SendMessage(g_hwndTT, TTM_ADDTOOL, 0,  (LPARAM) (LPTOOLINFO) &ti);

        if ((g_nViewMode==VIEW_MODE_RESTORE)||((g_nViewMode==VIEW_MODE_SMALL)))
        {
            ShowWindow(pKnob->GetHWND(),SW_HIDE);
        }
    
        CreateMuteButton(hwndOwner);
    }
    else
    {
        //fix for bug 886 ... turns off mute line in case of cdopt.dll failure
        SendMessage(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),WM_LED_MUTE,0,FALSE);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * CreateButtonWindows
// Put the transport control buttons onto the screen
////////////////////////////////////////////////////////////////////////////////////////////
void CreateButtonWindows(HWND hwndOwner)
{
    InitMButtons(hInst,hwndOwner);

    InitButtonProperties();

    for (int i = 0; i < NUM_BUTTONS; i++)
    {
	    DWORD wsTab = biButtons[i].fBlockTab ? 0 : WS_TABSTOP;

	    CMButton* pButton = NULL;
    
	    TCHAR szCaption[MAX_PATH];
	    LoadString(hInst,biButtons[i].id,szCaption,sizeof(szCaption)/sizeof(TCHAR));
	    
	    int x, y, width;
        int nView = SW_SHOW;

        switch (g_nViewMode)
        {
            case VIEW_MODE_NORMAL :
            {
                x = biButtons[i].uixy.x;
                y = biButtons[i].uixy.y;
                width = biButtons[i].width;
                if (biButtons[i].id == IDB_SET_NORMAL_MODE)
                {
                    nView = SW_HIDE;
                }
            }
            break;

            case VIEW_MODE_NOBAR :
            {
                x = biButtons[i].uixy.x;
                y = biButtons[i].uixy.y - 16;
                width = biButtons[i].width;
                if (biButtons[i].dwStyle == MBS_SYSTEMTYPE)
                {
                    nView = SW_HIDE;
                }
            }
            break;

            case VIEW_MODE_RESTORE :
            {
                x = biButtons[i].uixy2.x;
                y = biButtons[i].uixy2.y;
                width = biButtons[i].width2;
                if (biButtons[i].id == IDB_SET_TINY_MODE)
                {
                    nView = SW_HIDE;
                }
            }
            break;

            case VIEW_MODE_SMALL :
            {
                x = biButtons[i].uixy2.x;
                y = biButtons[i].uixy2.y - 12;
                width = biButtons[i].width2;
            }
            break;
        }

        //for buttons that aren't going to blit in smaller modes
        if (width == 0)
        {
            //set to normal width
            width = biButtons[i].width;
            nView = SW_HIDE;
        }

        pButton = CreateMButton(szCaption,biButtons[i].nIconID,WS_VISIBLE|wsTab,
				        biButtons[i].dwStyle,
				        x,
				        y,
				        width,
				        biButtons[i].height,
				        hwndOwner,
				        FALSE,  //create original, not subclass
				        biButtons[i].id,
                        biButtons[i].nToolTipID,
				        hInst);

        //hide system buttons in small mode
        if (g_nViewMode == VIEW_MODE_SMALL)
        {
            if (biButtons[i].dwStyle == MBS_SYSTEMTYPE)
            {
                ShowWindow(pButton->GetHWND(),SW_HIDE); 
            }
        }

        if (nView == SW_HIDE)
        {
            ShowWindow(pButton->GetHWND(),SW_HIDE);
        }

        TOOLINFO ti;
        ti.cbSize = sizeof(TOOLINFO); 
        ti.uFlags = TTF_IDISHWND;
        ti.hwnd =  hwndOwner; 
        ti.uId = (UINT_PTR)(pButton->GetHWND());
        ti.hinst = hInst; 
        ti.lpszText = LPSTR_TEXTCALLBACK;
        SendMessage(g_hwndTT, TTM_ADDTOOL, 0,  (LPARAM) (LPTOOLINFO) &ti);

	    //set focus and default to first control
	    if (i == 0)
	    {
	        SetFocus(pButton->GetHWND());
	    }
    } //end for buttons

    SendMessage(hwndOwner, DM_SETDEFID, biButtons[0].id, 0);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * VolPersistTimerProc
// When we're done displaying the volume, tell CD player to repaint normally
////////////////////////////////////////////////////////////////////////////////////////////
void CALLBACK VolPersistTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
    //turn on painting in the led window
    SendMessage(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),WM_LED_INFO_PAINT,1,0);
    InvalidateRect(hwndCurrentComp,NULL,FALSE);
    KillTimer(hwnd,idEvent);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * IsNetOK
// Returns TRUE if it is OK to do networking-related stuff (i.e. the database is all right)
////////////////////////////////////////////////////////////////////////////////////////////
BOOL IsNetOK(HWND hwnd)
{
    DWORD dwRet = FALSE;
    
    LPCDDATA pData = GetCDData();
    if (pData)
    {
        if (SUCCEEDED(pData->CheckDatabase(hwnd)))
        {
            dwRet = TRUE;
        }
    }

    return (dwRet);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * IsDownloading
// Check on the networking thread to see if it is active
////////////////////////////////////////////////////////////////////////////////////////////
BOOL IsDownloading()
{
    BOOL retcode = FALSE;
    
    ICDNet* pICDNet = NULL;
    if (SUCCEEDED(CDNET_CreateInstance(NULL, IID_ICDNet, (void**)&pICDNet)))
    {
		    retcode = pICDNet->IsDownloading();
            pICDNet->Release();
    }

    return (retcode);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * CancelDownload
// Tell the networking thread to quit as soon as it can
////////////////////////////////////////////////////////////////////////////////////////////
void CancelDownload()
{
    ICDNet* pICDNet = NULL;
    if (SUCCEEDED(CDNET_CreateInstance(NULL, IID_ICDNet, (void**)&pICDNet)))
    {
        pICDNet->CancelDownload();
        pICDNet->Release();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * EndDownloadThreads
// Kills the download threads on shutdown
////////////////////////////////////////////////////////////////////////////////////////////
void EndDownloadThreads()
{
    //optimization: don't bother if CDNET.DLL is not loaded
    if (GetModuleHandle(TEXT("CDNET.DLL")))
    {
        if (IsDownloading())
        {
            CancelDownload();
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * MenuButtonTimerProc
// Big ol' hack to make the menus that drop down from button seem like real menus ...
// if the user hits the button before the timeout time, we don't redisplay the menu
////////////////////////////////////////////////////////////////////////////////////////////
void CALLBACK MenuButtonTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
    //single-shot timer turns off the "block menu" flag
    fBlockMenu = FALSE;
    nLastMenu = 0;
    KillTimer(hwnd,idEvent);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * BlockMenu
// Turns on the button/menu hack
////////////////////////////////////////////////////////////////////////////////////////////
void BlockMenu(HWND hwnd)
{
    fBlockMenu = TRUE;
    SetTimer(hwnd,nLastMenu,MENU_TIMER_RATE,(TIMERPROC)MenuButtonTimerProc);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * NormalizeNameForMenuDisplay
//    This function turns a string like "Twist & Shout" into
//    "Twist && Shout" because otherwise it will look like
//    "Twist _Shout" in the menu due to the accelerator char
//
//  Defined in cdplayer.lib
//
////////////////////////////////////////////////////////////////////////////////////////////
extern "C" void NormalizeNameForMenuDisplay(TCHAR* szInput, TCHAR* szOutput, DWORD cbLen);

////////////////////////////////////////////////////////////////////////////////////////////
// * DrawButton
// Response to WM_DRAWITEM on buttons
////////////////////////////////////////////////////////////////////////////////////////////
void DrawButton(UINT idCtl, LPDRAWITEMSTRUCT lpdis)
{
    CMButton* pButton = GetMButtonFromID(hwndMain,idCtl);

    if (pButton!=NULL)
    {
	    pButton->Draw(lpdis);
    }

    //special case ... if the button is one of the scanner buttons,
    //forward this message to the component
    if ((idCtl==IDB_REW) || (idCtl==IDB_FFWD))
    {
        switch (idCtl)
        {
            case IDB_REW  : idCtl = IDM_PLAYBAR_SKIPBACK; break;
            case IDB_FFWD : idCtl = IDM_PLAYBAR_SKIPFORE; break;
        }
        lpdis->CtlID = idCtl;
        SendMessage(hwndCurrentComp,WM_DRAWITEM,idCtl,(LPARAM)lpdis);
    }

    if (
        (idCtl == IDB_OPTIONS) ||
        (idCtl == IDB_MODE) ||
        (idCtl == IDB_TRACK) ||
        (idCtl == IDB_NET) ||
        (idCtl == IDB_DISC)
       )
    {
        if (lpdis->itemState & ODS_SELECTED)
        {
            if ((fBlockMenu) && (nLastMenu == idCtl))
            {
                return;
            }

            HWND hwnd = hwndMain;
	        RECT rect;
            AllocCustomMenu(&g_pMenu);
            CustomMenu* pSearchSubMenu = NULL;
            CustomMenu* pProviderSubMenu = NULL;

            if (!g_pMenu)
            {
                return;
            }

            if (idCtl == IDB_OPTIONS)
            {
                g_pMenu->AppendMenu(IDM_OPTIONS,hInst,IDM_OPTIONS);
                g_pMenu->AppendMenu(IDM_PLAYLIST,hInst,IDM_PLAYLIST);
                g_pMenu->AppendSeparator();

                if (!IsNetOK(hwnd))
                {
                    EnableMenuItem(g_pMenu->GetMenuHandle(),
                                    IDM_PLAYLIST,
                                    MF_BYCOMMAND | MF_GRAYED);
                }

                if (g_nViewMode == VIEW_MODE_NORMAL)
                {
                    g_pMenu->AppendMenu(IDM_TINY,hInst,IDM_TINY);
                }
                else
                {
                    g_pMenu->AppendMenu(IDM_NORMAL,hInst,IDM_NORMAL);
                }

                g_pMenu->AppendSeparator();

                g_pMenu->AppendMenu(IDM_HELP,hInst,IDM_HELP);
                g_pMenu->AppendMenu(IDM_ABOUT,hInst,IDM_ABOUT);

                g_pMenu->AppendSeparator();

                g_pMenu->AppendMenu(IDM_EXIT,hInst,IDM_EXIT);
            } //end if options

            if (idCtl == IDB_NET)
            {
                AllocCustomMenu(&pSearchSubMenu);
                AllocCustomMenu(&pProviderSubMenu);

		        MMMEDIAID mmMedia;
                mmMedia.nDrive = -1;
	            IMMComponentAutomation* pAuto = NULL;
	            HRESULT hr = pNodeCurrent->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&pAuto);
		        pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);
                pAuto->Release();

                BOOL fContinue = TRUE;

                //append static menu choices
                if (IsDownloading())
                {
                    g_pMenu->AppendMenu(IDM_NET_CANCEL,hInst,IDM_NET_CANCEL);
                }
                else
                {
                    g_pMenu->AppendMenu(IDM_NET_UPDATE,hInst,IDM_NET_UPDATE);
                    if (mmMedia.dwMediaID == 0)
                    {
                        //need to gray out menu
                        MENUITEMINFO mmi;
                        mmi.cbSize = sizeof(mmi);
                        mmi.fMask = MIIM_STATE;
                        mmi.fState = MFS_GRAYED;
                        HMENU hMenu = g_pMenu->GetMenuHandle();
                        SetMenuItemInfo(hMenu,IDM_NET_UPDATE,FALSE,&mmi);
                    }
                }
	            
                //if networking is not allowed, gray it out ...
                //don't worry about cancel case, it won't be there
                if (!IsNetOK(hwnd))
                {
                    EnableMenuItem(g_pMenu->GetMenuHandle(),
                                    IDM_NET_UPDATE,
                                    MF_BYCOMMAND | MF_GRAYED);
                }

                //don't allow searching if title isn't available
                LPCDDATA pData = GetCDData();
                if (pData)
                {
                    if (pData->QueryTitle(mmMedia.dwMediaID))
                    {
                        pSearchSubMenu->AppendMenu(IDM_NET_BAND,hInst,IDM_NET_BAND);
                        pSearchSubMenu->AppendMenu(IDM_NET_CD,hInst,IDM_NET_CD);
                        pSearchSubMenu->AppendMenu(IDM_NET_ROLLINGSTONE_ARTIST,hInst,IDM_NET_ROLLINGSTONE_ARTIST);
                        pSearchSubMenu->AppendMenu(IDM_NET_BILLBOARD_ARTIST,hInst,IDM_NET_BILLBOARD_ARTIST);
                        pSearchSubMenu->AppendMenu(IDM_NET_BILLBOARD_ALBUM,hInst,IDM_NET_BILLBOARD_ALBUM);
                        g_pMenu->AppendMenu(hInst,IDM_NET_SEARCH_HEADING,pSearchSubMenu);
                    }
                } //end if pdata

                //display any provider home pages
                DWORD i = 0;
                LPCDOPT pOpt = GetCDOpt();
                if( pOpt )
                {
                    LPCDOPTIONS pCDOpts = pOpt->GetCDOpts();

                    LPCDPROVIDER pProviderList = pCDOpts->pProviderList;

                    while (pProviderList!=NULL)
                    {
                        TCHAR szProviderMenu[MAX_PATH];
                        TCHAR szHomePageFormat[MAX_PATH/2];
                        LoadString(hInst,IDS_HOMEPAGEFORMAT,szHomePageFormat,sizeof(szHomePageFormat)/sizeof(TCHAR));
                        wsprintf(szProviderMenu,szHomePageFormat,pProviderList->szProviderName);
    
                        pProviderSubMenu->AppendMenu(IDM_HOMEMENU_BASE+i,szProviderMenu);

                        pProviderList = pProviderList->pNext;
                        i++;
                    } //end while

                    g_pMenu->AppendMenu(hInst,IDM_NET_PROVIDER_HEADING,pProviderSubMenu);
                } //end home pages

                //display internet-loaded disc menus
                if (pData)
                {
                    if (pData->QueryTitle(mmMedia.dwMediaID))
                    {
                        LPCDTITLE pCDTitle = NULL;
                        hr =  pData->LockTitle(&pCDTitle,mmMedia.dwMediaID);

                        if (SUCCEEDED(hr))
                        {
                            for (i = 0; i < pCDTitle->dwNumMenus; i++)
                            {
                                if (i==0)
                                {
                                    g_pMenu->AppendSeparator();
                                }

                                TCHAR szDisplayNet[MAX_PATH];
                                NormalizeNameForMenuDisplay(pCDTitle->pMenuTable[i].szMenuText,szDisplayNet,sizeof(szDisplayNet));
            	                g_pMenu->AppendMenu(i + IDM_NETMENU_BASE,szDisplayNet);
                            }

                            pData->UnlockTitle(pCDTitle,FALSE);
                        }
                    } //end if query title
                }
            } //end if net

            if (idCtl == IDB_MODE)
            {
                g_pMenu->AppendMenu(IDM_MODE_NORMAL,hInst,IDI_MODE_NORMAL,IDM_MODE_NORMAL);
                g_pMenu->AppendMenu(IDM_MODE_RANDOM,hInst,IDI_MODE_RANDOM,IDM_MODE_RANDOM);
                g_pMenu->AppendMenu(IDM_MODE_REPEATONE,hInst,IDI_MODE_REPEATONE,IDM_MODE_REPEATONE);
                g_pMenu->AppendMenu(IDM_MODE_REPEATALL,hInst,IDI_MODE_REPEATALL,IDM_MODE_REPEATALL);
                g_pMenu->AppendMenu(IDM_MODE_INTRO,hInst,IDI_MODE_INTRO,IDM_MODE_INTRO);
                g_pMenu->SetMenuDefaultItem(nCDMode,FALSE);
            } //end if mode

            if (idCtl==IDB_TRACK)
            {
	            IMMComponentAutomation* pAuto = NULL;
	            HRESULT hr = pNodeCurrent->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&pAuto);
	            if ((SUCCEEDED(hr)) && (pAuto != NULL))
	            {
                    int i = 0;
                    while (SUCCEEDED(hr))
                    {
		                MMTRACKORDISC mmTrack;
                        mmTrack.nNumber = i++;
                        hr = pAuto->OnAction(MMACTION_GETTRACKINFO,&mmTrack);
                        if (SUCCEEDED(hr))
                        {
                            g_pMenu->AppendMenu(mmTrack.nID + IDM_TRACKLIST_BASE, mmTrack.szName);
                            if (mmTrack.fCurrent)
                            {
                                g_pMenu->SetMenuDefaultItem(mmTrack.nID + IDM_TRACKLIST_BASE,FALSE);
                            } //end if current
                        } //end if ok
                    } //end while
                    pAuto->Release();
                }
            } //end if track

            if (idCtl == IDB_DISC)
            {
	            IMMComponentAutomation* pAuto = NULL;
	            HRESULT hr = pNodeCurrent->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&pAuto);
	            if ((SUCCEEDED(hr)) && (pAuto != NULL))
	            {
                    int i = 0;
                    while (SUCCEEDED(hr))
                    {
		                MMTRACKORDISC mmDisc;
                        mmDisc.nNumber = i++;
                        hr = pAuto->OnAction(MMACTION_GETDISCINFO,&mmDisc);
                        if (SUCCEEDED(hr))
                        {
                            g_pMenu->AppendMenu(mmDisc.nID + IDM_DISCLIST_BASE, mmDisc.szName);
                            if (mmDisc.fCurrent)
                            {
                                g_pMenu->SetMenuDefaultItem(mmDisc.nID + IDM_DISCLIST_BASE,FALSE);
                            } //end if current
                        }
                    }
                    pAuto->Release();
                }
            } //end if disc

            //push down to under button
            HWND hwndButton = pButton->GetHWND();
	        GetClientRect(hwndButton,&rect);

	        //convert whole rect to screen coordinates
            ClientToScreen(hwndButton,(LPPOINT)&rect);
            ClientToScreen(hwndButton,((LPPOINT)&rect)+1);

            KillTimer(hwnd,nLastMenu);
            nLastMenu = idCtl;
            fBlockMenu = TRUE;
            pButton->SetMenuingState(TRUE);
            if (g_pMenu)
            {
                g_pMenu->TrackPopupMenu(0,rect.left,rect.bottom,hwnd,&rect);
            }
            else
            {
                BlockMenu(hwnd);
            }
            pButton->SetMenuingState(FALSE);
    
            if (g_pMenu)
            {
                g_pMenu->Destroy();
                g_pMenu = NULL;
            }
    
            if (pProviderSubMenu)
            {
                pProviderSubMenu->Destroy();
                pProviderSubMenu = NULL;
            }
            if (pSearchSubMenu)
            {
                pSearchSubMenu->Destroy();
                pSearchSubMenu = NULL;
            }
        } //end if selected
    } //end if right button

    return;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * OnNCHitTest
// How we pretend that we have a real caption
////////////////////////////////////////////////////////////////////////////////////////////
UINT OnNCHitTest(HWND hwnd, short x, short y, BOOL fButtonDown)
{
    UINT ht = HTCLIENT;

    if (!fButtonDown)
    {
        ht = FORWARD_WM_NCHITTEST(hwnd, x, y, DefWindowProc );
    }

    RECT rect;
    GetClientRect(hwnd,&rect);
    rect.bottom = rect.top + TITLEBAR_HEIGHT +
                    (g_nViewMode == VIEW_MODE_NORMAL ? TITLEBAR_YOFFSET_LARGE :
                                                       TITLEBAR_YOFFSET_SMALL);

    POINT pt;
    pt.x = (LONG)x;
    pt.y = (LONG)y;

    ScreenToClient(hwnd,&pt);

    if (PtInRect(&rect,pt))
    {
        ht = HTCAPTION;
    }

    rect.left = SYSMENU_XOFFSET;
    rect.right = SYSMENU_XOFFSET + SYSMENU_WIDTH;
    //check for a system-menu hit.
    if (PtInRect(&rect,pt))
    {
        ht = HTSYSMENU;
    }

    //always a caption hit in small mode or nobar mode
    if (g_nViewMode >= VIEW_MODE_SMALL)
    {
        ht = HTCAPTION;
    }

    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, ht);

    return (ht);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * FillGradient
// from kernel's caption.c
// Allows us to have the cool gradient caption bar that you get for free otherwise
////////////////////////////////////////////////////////////////////////////////////////////
void FillGradient(HDC hdc, LPCRECT prc, COLORREF rgbLeft, COLORREF rgbRight)
{
    TRIVERTEX avert[2];
    static GRADIENT_RECT auRect[1] = {0,1};
    #define GetCOLOR16(RGB, clr) ((COLOR16)(Get ## RGB ## Value(clr) << 8))

    avert[0].Red = GetCOLOR16(R, rgbLeft);
    avert[0].Green = GetCOLOR16(G, rgbLeft);
    avert[0].Blue = GetCOLOR16(B, rgbLeft);

    avert[1].Red = GetCOLOR16(R, rgbRight);
    avert[1].Green = GetCOLOR16(G, rgbRight);
    avert[1].Blue = GetCOLOR16(B, rgbRight);

    avert[0].x = prc->left;
    avert[0].y = prc->top;
    avert[1].x = prc->right;
    avert[1].y = prc->bottom;

    //only load once, when needed.  Freed in "CleanUp" call
    if (hmImage == NULL)
    {
        hmImage = LoadLibrary(TEXT("MSIMG32.DLL"));
        if (hmImage!=NULL)
        {
	        fnGradient = (GRADIENTPROC)GetProcAddress(hmImage,"GradientFill");
        }
    }

	if (fnGradient!=NULL)
	{
		fnGradient(hdc, avert, 2, (PUSHORT)auRect, 1, 0x00000000);
        return;
	}

    BOOL fActiveWindow = FALSE;

    if (hwndMain == GetForegroundWindow())
    {
	    fActiveWindow = TRUE;
    }

	HBRUSH hbrush = CreateSolidBrush(GetSysColor(fActiveWindow ? COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION));
	FillRect(hdc,prc,hbrush);
	DeleteObject(hbrush);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * DrawTitleBar
// Blits the title bar to the screen
////////////////////////////////////////////////////////////////////////////////////////////
void DrawTitleBar(HDC hdc, HWND hwnd, BOOL fActiveWindow, BOOL fExludeRect)
{
    if (g_nViewMode >= VIEW_MODE_SMALL)
    {
        return; //no title bar in these views
    }

    RECT rect;
    HBRUSH hbrush;

    //convert the client rect of the minimize button into the rect within the
    //main display area
    RECT minButtonRect;
    RECT mainWndRect;
    GetWindowRect(hwnd,&mainWndRect);
    
    HWND hwndButton = GetDlgItem(hwnd,IDB_MINIMIZE);

    if (!hwndButton)
    {
        return; //must have been called before button was created
    }

    GetWindowRect(hwndButton,&minButtonRect);
    
    HDC memDC = CreateCompatibleDC(hdc);
    HBITMAP hbmp = CreateCompatibleBitmap(hdc,
					  (g_nViewMode == VIEW_MODE_NORMAL ? bmMain.bmWidth : bmMainRestore.bmWidth),
					  (g_nViewMode == VIEW_MODE_NORMAL ? bmMain.bmHeight : bmMainRestore.bmHeight));
    HBITMAP holdbmp = (HBITMAP)SelectObject(memDC, hbmp);

    BOOL fGradient = FALSE;
    SystemParametersInfo(SPI_GETGRADIENTCAPTIONS,0,&fGradient,0);

    //we just need the left-hand side.
    //to get it, we take the width of the window and subtract the offset
    //from the right of the window
    minButtonRect.left = (mainWndRect.right - mainWndRect.left) -
                         (mainWndRect.right - minButtonRect.left);

    rect.left = SYSMENU_XOFFSET + SYSMENU_WIDTH + 1;
    rect.right = minButtonRect.left - (GetXOffset()*2) - 1;

    rect.top = (g_nViewMode == VIEW_MODE_NORMAL ? TITLEBAR_YOFFSET_LARGE :
                                                  TITLEBAR_YOFFSET_SMALL);

    rect.bottom = rect.top + TITLEBAR_HEIGHT;

    if (fGradient)
    {
	    DWORD dwStartColor = GetSysColor(fActiveWindow ? COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
	    DWORD dwFinishColor = GetSysColor(fActiveWindow ? COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
	    FillGradient(memDC,&rect,dwStartColor,dwFinishColor);
    }
    else
    {
	    hbrush = CreateSolidBrush(GetSysColor(fActiveWindow ? COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION));
	    FillRect(memDC,&rect,hbrush);
	    DeleteObject(hbrush);
    }

    TCHAR s[MAX_PATH];
    GetWindowText(hwnd,s,MAX_PATH-1);

    SetBkMode(memDC,TRANSPARENT);
    SetTextColor(memDC, GetSysColor(fActiveWindow ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));

    //create title bar font
    NONCLIENTMETRICS metrics;
    metrics.cbSize = sizeof(metrics);
    SystemParametersInfo(SPI_GETNONCLIENTMETRICS,sizeof(metrics),&metrics,0);

    if (IS_DBCS_CHARSET(metrics.lfCaptionFont.lfCharSet))
    {
	metrics.lfCaptionFont.lfHeight = (-9 * STANDARD_PIXELS_PER_INCH) / 72;
    } else {
	metrics.lfCaptionFont.lfHeight = (-8 * STANDARD_PIXELS_PER_INCH) / 72;
    }

    HFONT hTitleFont = CreateFontIndirect(&metrics.lfCaptionFont);

    HFONT hOrgFont = (HFONT)SelectObject(memDC, hTitleFont);

    ExtTextOut( memDC, rect.left + 3, rect.top, 0, NULL, s, _tcslen(s), NULL );

    SelectObject(memDC,hOrgFont);

    BitBlt(hdc,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top-1,memDC,rect.left,rect.top,SRCCOPY);

    if (fExludeRect)
    {
        ExcludeClipRect(hdc,rect.left,rect.top,rect.right,rect.bottom-1);        
    }

    SelectObject(memDC, holdbmp);
    DeleteObject(hbmp);
    SelectObject(memDC, hOrgFont);
    DeleteDC(memDC);
    if (hTitleFont)
        DeleteObject(hTitleFont);
}
    
////////////////////////////////////////////////////////////////////////////////////////////
// * DrawVolume
// Tells the cdplayer to start showing the volume setting
////////////////////////////////////////////////////////////////////////////////////////////
void DrawVolume(DWORD level)
{
    //we just have the led window draw it
    HWND ledWnd = GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW);

    MMONVOLCHANGED mmVolChange;
    mmVolChange.dwNewVolume = level;
    mmVolChange.fMuted = FALSE;
    mmVolChange.szLineName = szLineName;

    SendMessage(ledWnd,WM_LED_INFO_PAINT,0,(LPARAM)&mmVolChange);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * OnToolTipNotify
// Called from tool tips to get the text they need to display
////////////////////////////////////////////////////////////////////////////////////////////
VOID OnToolTipNotify(LPARAM lParam)
{     
    LPTOOLTIPTEXT lpttt;     
    HWND hwndCtrl;  

    if ((((LPNMHDR) lParam)->code) == TTN_NEEDTEXT) 
    { 
        hwndCtrl = (HWND)((LPNMHDR)lParam)->idFrom; 
        lpttt = (LPTOOLTIPTEXT) lParam;

        if (hwndCtrl == GetDlgItem(hwndMain,IDB_VOLUME))
        {
            GetWindowText(hwndCtrl,g_tooltext,sizeof(g_tooltext)/sizeof(TCHAR));
        }
        else
        {
            CMButton* pButton = GetMButtonFromHWND(hwndCtrl);
            if (pButton)
            {
                LoadString(hInst,pButton->GetToolTipID(),g_tooltext,sizeof(g_tooltext)/sizeof(TCHAR));
            }
        }
        lpttt->lpszText = g_tooltext;
    } 	
    return;
} 

////////////////////////////////////////////////////////////////////////////////////////////
// * GetNumBatchedTitles
// Get the number of titles currently in the batch queue
////////////////////////////////////////////////////////////////////////////////////////////
DWORD GetNumBatchedTitles()
{
    LPCDDATA pData = GetCDData();
    DWORD dwReturn = 0;

    if (pData)
    {
        dwReturn = pData->GetNumBatched();
    }

    return (dwReturn);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * HandleBadServiceProvider
// Put up message box if the provider did not pass validation
////////////////////////////////////////////////////////////////////////////////////////////
void HandleBadServiceProvider(HWND hwndParent)
{
    TCHAR szError[MAX_PATH];
    LoadString(hInst,IDS_BADPROVIDER,szError,sizeof(szError)/sizeof(TCHAR));
    MessageBox(hwndParent,szError,szAppName,MB_ICONEXCLAMATION|MB_OK);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * NormalizeNameForURL
// Changes a name to a "normalized" name for URL
////////////////////////////////////////////////////////////////////////////////////////////
void NormalizeNameForURL(LPCTSTR szName, LPTSTR szOutput, DWORD cbOutputLen)
{
	typedef BOOL (PASCAL *CANPROC)(LPCTSTR, LPTSTR, LPDWORD, DWORD);
    CANPROC canProc = NULL;
    
    _tcscpy(szOutput,szName); //init URL with passed-in value

    //if possible, canonicalize the URL
    HMODULE hNet = LoadLibrary(TEXT("WININET.DLL"));
    if (hNet!=NULL)
    {
	    canProc = (CANPROC)GetProcAddress(hNet,CANONFUNCTION);
        if (canProc!=NULL)
        {
            BOOL f = canProc(szName,szOutput,&cbOutputLen,0);
        }
        FreeLibrary(hNet);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * OpenBrowserURL
// Opens a URL in the default browser
////////////////////////////////////////////////////////////////////////////////////////////
void OpenBrowserURL(TCHAR* szURL)
{
    ShellExecute(NULL,_TEXT("open"),szURL,NULL,_TEXT(""),SW_NORMAL);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * ProgressDlgProc
// Main proc for download progress dialog
// bugbug: Put in own file
////////////////////////////////////////////////////////////////////////////////////////////
INT_PTR CALLBACK ProgressDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	BOOL fReturnVal = TRUE;
    static HWND hwndAnimate = NULL;
    static HANDLE hLogo = NULL;
    static LPCDOPT pOpts = NULL;
    static fOneDownloaded = FALSE;
    static LPCDPROVIDER pCurrent = NULL;
		
    switch (msg) 
    { 
    	default:
			fReturnVal = FALSE;
		break;
		
        case WM_INITDIALOG:
        {
            fOneDownloaded = FALSE;
            BOOL fSingle = FALSE;
            DWORD dwDiscID = (DWORD)-1; //use batch

            if (lParam == 0)
            {
                if (!pSingleTitle)
                {
                    EndDialog(hDlg,-1);
                    return FALSE;
                }

                fSingle = TRUE;
                dwDiscID = pSingleTitle->dwTitleID;
                lParam = 1;
            }

            if (IsDownloading())
            {
                //if we're downloading on the Main UI, put up a "waiting" message for now
                TCHAR szWaiting[MAX_PATH];
                LoadString(hInst,IDS_WAITINGFORDOWNLOAD,szWaiting,sizeof(szWaiting)/sizeof(TCHAR));
                SendDlgItemMessage(hDlg,IDC_STATIC_INFO,WM_SETTEXT,0,(LPARAM)szWaiting);
            }

            //set the range
        	SendDlgItemMessage(hDlg, IDC_METER, PBM_SETRANGE, 0, MAKELPARAM(0, lParam));
            SendDlgItemMessage(hDlg, IDC_METER, PBM_SETPOS, 0, 0);

            //proceed with the download
            MMNET mmNet;
            mmNet.discid = dwDiscID;
            mmNet.hwndCallback = hDlg; //call back to this guy
            mmNet.fForceNet = fSingle;
            mmNet.pData = (void*)GetCDData();
            if (fSingle)
            {
                mmNet.pData2 = (void*)pSingleTitle;
            }
            else
            {
                mmNet.pData2 = NULL;
            }
            SendMessage(hwndCurrentComp,WM_COMMAND,MAKEWPARAM(ID_CDUPDATE,0),(LPARAM)&mmNet);

            pOpts = GetCDOpt();
            
            if (!hLogo)
            {
                //get the path to the vendor logo file
                if (pOpts)
                {
                    LPCDOPTIONS pOptions = NULL;
                    pOptions = pOpts->GetCDOpts();

                    if (pOptions)
                    {
                        if (pOptions->pCurrentProvider!=NULL)
                        {
                            hLogo = OpenDIB(pOptions->pCurrentProvider->szProviderLogo,(HFILE)-1);
                            pCurrent = pOptions->pCurrentProvider;
                        } //end if current provider ok
                    } //end if poptions ok
                } //end if popts created
            }

            fReturnVal = TRUE; 
        }		
		break;

        case WM_PAINT :
        {
	        HDC hdc;
	        PAINTSTRUCT ps;

	        hdc = BeginPaint( hDlg, &ps );
    
            RECT progressrect, mainrect;
            GetWindowRect(GetDlgItem(hDlg,IDC_METER),&progressrect);
            GetWindowRect(hDlg,&mainrect);
            mainrect.top = mainrect.top + GetSystemMetrics(SM_CYCAPTION);

            //turn on animation if it is not visible
            if (!hwndAnimate)
            {
                hwndAnimate = Animate_Create(hDlg,
                                            IDI_ICON_ANI_DOWN,
                                            WS_CHILD|ACS_TRANSPARENT,
                                            hInst);

                //headers don't have Animate_OpenEx yet,
                //so just do the straight call
                SendMessage(hwndAnimate,ACM_OPEN,(WPARAM)hInst,
                        (LPARAM)MAKEINTRESOURCE(IDI_ICON_ANI_DOWN));

                //move to the top/left of the window, equidistant from top and progress indicator
                RECT anirect;
                GetWindowRect(hwndAnimate,&anirect);
                MoveWindow(hwndAnimate,
                     progressrect.left - mainrect.left - 3,
                     ((progressrect.top - mainrect.top)
                        - (anirect.bottom - anirect.top)) - LOGO_Y_OFFSET,
                     anirect.right - anirect.left,
                     anirect.bottom - anirect.top,
                     FALSE);

                Animate_Play(hwndAnimate,0,-1,-1);
                ShowWindow(hwndAnimate,SW_SHOW);

                //move "info" window                
                MoveWindow(GetDlgItem(hDlg,IDC_STATIC_INFO),
                          (progressrect.left - mainrect.left) + (anirect.right - anirect.left) + 3,
                          ((progressrect.top - mainrect.top)
                                - (anirect.bottom - anirect.top)) - LOGO_Y_OFFSET,
                          ((progressrect.right - mainrect.left) - VENDORLOGO_WIDTH - 3)
                                - ((progressrect.left - mainrect.left) + (anirect.right - anirect.left) + 3),
                          anirect.bottom - anirect.top,
                          FALSE);
            }

            if (hLogo)
            {
                DibBlt(hdc,
                        (progressrect.right - mainrect.left) - (VENDORLOGO_WIDTH + 5),
                        ((progressrect.top - mainrect.top)
                            - VENDORLOGO_HEIGHT) - LOGO_Y_OFFSET,
                        -1, 
                        -1, 
                        hLogo,
                        0,0,
                        SRCCOPY,0);
            }

	        EndPaint(hDlg,&ps);
            return 0;
        }
        break;

        //try to launch provider home page
        case WM_LBUTTONUP :
        {
            if ((hLogo) && (pCurrent))
            {
                RECT progressrect, mainrect;
                GetWindowRect(GetDlgItem(hDlg,IDC_METER),&progressrect);
                GetWindowRect(hDlg,&mainrect);
                mainrect.top = mainrect.top + GetSystemMetrics(SM_CYCAPTION);

                RECT logoRect;
                SetRect(&logoRect,
                        (progressrect.right - mainrect.left) - VENDORLOGO_WIDTH,
                        ((progressrect.top - mainrect.top)
                            - VENDORLOGO_HEIGHT) - LOGO_Y_OFFSET,
                        (progressrect.right - mainrect.left),
                        (progressrect.top - mainrect.top) - LOGO_Y_OFFSET);

                POINT pt;
                pt.x = LOWORD(lParam);
                pt.y = HIWORD(lParam);
        
                if (PtInRect(&logoRect,pt))
                {
                    OpenBrowserURL(pCurrent->szProviderHome);
                }
            }
        }
        break;

        case WM_DESTROY :
        {
            if (hwndAnimate)
            {
                DestroyWindow(hwndAnimate);
                hwndAnimate = NULL;
            }

            if (hLogo)
            {
                GlobalFree(hLogo);
                hLogo = NULL;
            }
        }
        break;

        case WM_NET_DB_UPDATE_BATCH :
        {
            LPCDOPT pOpts = GetCDOpt();
            if (pOpts)
            {
                pOpts->DownLoadCompletion(0,NULL);
            }
        }
        break;

        case WM_NET_DB_UPDATE_DISC :
        {
            LPCDOPT pOpts = GetCDOpt();
            if (pOpts)
            {
                pOpts->DiscChanged((LPCDUNIT)lParam);
            }
        }
        break;

        case WM_NET_CHANGEPROVIDER :
        {
            LPCDPROVIDER pProv = (LPCDPROVIDER)lParam;
            if (pProv!=NULL)
            {
                pCurrent = pProv;

                if (hLogo)
                {
                    GlobalFree(hLogo);
                    hLogo = NULL;
                }

                hLogo = OpenDIB(pCurrent->szProviderLogo,(HFILE)-1);
                InvalidateRect(hDlg,NULL,FALSE);
                UpdateWindow(hDlg);
            } //end if provider ok
        }
        break;

        case WM_NET_STATUS :
        {
            //until at least one title is downloaded, we need to do
            //something to entertain the user, so go ahead and show the
            //downloading text
            if (!fOneDownloaded)
            {
                TCHAR progstr[MAX_PATH];
                LoadString((HINSTANCE)wParam,(UINT)lParam,progstr,sizeof(progstr)/sizeof(TCHAR));
                SendDlgItemMessage(hDlg,IDC_STATIC_INFO,WM_SETTEXT,0,(LPARAM)progstr);
            }
        }
        break;

        case WM_NET_DONE :
        {
            fOneDownloaded = TRUE;

            if (lParam == (LPARAM)-1)
            {
                HandleBadServiceProvider(hDlg);
                EndDialog(hDlg,0);
                break;
            }

            if (lParam == 0)
            {
                EndDialog(hDlg,0);
                break;
            }
            else
            {
                MMNET mmNet;
                mmNet.discid = (DWORD)lParam;
                mmNet.hwndCallback = hDlg;
                mmNet.pData = (void*)GetCDData();
                mmNet.pData2 = NULL;
                mmNet.fForceNet = FALSE;
                SendMessage(hwndCurrentComp,WM_COMMAND,MAKEWPARAM(ID_CDUPDATE,0),(LPARAM)&mmNet);

                LPCDDATA pData = GetCDData();
                                
                //try to display that we found a title
                if (!pData)
                {
                    break;
                }
                
                //
                // Try to read in title from the options database
                //

                if (!pData->QueryTitle((DWORD)(lParam)))
                {
                    break;
                }

                //
                // We found an entry for this disc, so copy all the information
                // from the title database

                LPCDTITLE pCDTitle = NULL;

                if (FAILED(pData->LockTitle(&pCDTitle,(DWORD)(lParam))))
                {
                    break;
                }

                TCHAR foundstr[MAX_PATH];
                TCHAR formatstr[MAX_PATH];
                LoadString(hInst,IDS_FOUND,formatstr,sizeof(formatstr)/sizeof(TCHAR));
                wsprintf(foundstr,formatstr,pCDTitle->szTitle,pCDTitle->szArtist);
                SendDlgItemMessage(hDlg,IDC_STATIC_INFO,WM_SETTEXT,0,(LPARAM)foundstr);
            }
        }
        break;

        case WM_NET_INCMETER :
        {
            LRESULT dwPos = SendDlgItemMessage(hDlg,IDC_METER,PBM_GETPOS,0,0);
            SendDlgItemMessage(hDlg, IDC_METER, PBM_SETPOS, (WPARAM)++dwPos, 0);
        }
        break;

        case WM_NET_DB_FAILURE :
        {
            TCHAR szDBError[MAX_PATH];
            LoadString(hInst,IDS_DB_FAILURE,szDBError,sizeof(szDBError)/sizeof(TCHAR));
            MessageBox(hDlg,szDBError,szAppName,MB_ICONERROR|MB_OK);
        }
        break;

        case WM_NET_NET_FAILURE :
        {
            TCHAR szNetError[MAX_PATH];
            LoadString(hInst,IDS_NET_FAILURE,szNetError,sizeof(szNetError)/sizeof(TCHAR));
            MessageBox(hDlg,szNetError,szAppName,MB_ICONERROR|MB_OK);
        }
        break;

        case WM_COMMAND :
        {
            if (LOWORD(wParam) == IDCANCEL)
            {
                CancelDownload();
                EndDialog(hDlg,-1);
            }
        }
    }

    return fReturnVal;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * HandleDiscsNotFound
// Ask the user if they want to nuke unfound batched titles, or save them for another time
////////////////////////////////////////////////////////////////////////////////////////////
void HandleDiscsNotFound(HWND hwndParent)
{
    DWORD dwNumNotFound =  GetNumBatchedTitles();

    if (dwNumNotFound == 0)
    {
        return;
    }

    TCHAR szNotFound[MAX_PATH];

    if (dwNumNotFound > 1)
    {
        TCHAR szFormat[MAX_PATH];
        LoadString(hInst,IDS_NOTFOUND,szFormat,sizeof(szFormat)/sizeof(TCHAR));

        wsprintf(szNotFound,szFormat,dwNumNotFound);
    }
    else
    {
        LoadString(hInst,IDS_NOTFOUND1,szNotFound,sizeof(szNotFound)/sizeof(TCHAR));
    }

    int nAnswer = MessageBox(hwndParent,szNotFound,szAppName,MB_YESNO|MB_ICONQUESTION);

    if (nAnswer == IDNO)
    {
        LPCDDATA pData = GetCDData();
        if (pData)
        {
            pData->DumpBatch();
        }
    } //if said "no" to keeping batch
}

////////////////////////////////////////////////////////////////////////////////////////////
// * OptionsDownloadCallback
// Called from the options dialog to do batches or single downloads
////////////////////////////////////////////////////////////////////////////////////////////
DWORD CALLBACK OptionsDownloadCallback(LPCDTITLE pTitle, LPARAM lParam, HWND hwndParent)
{
    if (pTitle == NULL)
    {
        DWORD dwNumBatched = GetNumBatchedTitles();

        if (dwNumBatched > 0)
        {
            DialogBoxParam(hInst,MAKEINTRESOURCE(IDD_DOWNLOADPROGRESS),
                           hwndParent,ProgressDlgProc,dwNumBatched);
        }

        HandleDiscsNotFound(hwndParent);

        //refresh the number of batched titles
        return GetNumBatchedTitles();
    }
    else
    {
        pSingleTitle = pTitle;
        INT_PTR status = DialogBoxParam(hInst,MAKEINTRESOURCE(IDD_DOWNLOADPROGRESS),
                       hwndParent,ProgressDlgProc,0);

        //update the UI
        LPCDOPT pOpts = GetCDOpt();
        if (pOpts)
        {
            LPCDUNIT pUnit = pOpts->GetCDOpts()->pCDUnitList;

            while (pUnit!=NULL)
            {
                if (pUnit->dwTitleID == pSingleTitle->dwTitleID)
                {
                    pUnit->fDownLoading = FALSE;
                    pOpts->DiscChanged(pUnit);
                    break;
                }
                pUnit = pUnit->pNext;
            }
        }

        //if download wasn't canceled, check for disc id in database
        if (status != -1)
        {
            LPCDDATA pData = GetCDData();
            if (!pData->QueryTitle(pSingleTitle->dwTitleID))
            {
                TCHAR szNotFound[MAX_PATH];
                LoadString(hInst,IDS_TITLE_NOT_FOUND,szNotFound,sizeof(szNotFound)/sizeof(TCHAR));
                MessageBox(hwndParent,szNotFound,szAppName,MB_ICONINFORMATION|MB_OK);
            }
            else
            {
                if (pOpts)
                {
                    LPCDOPTIONS pCDOpts = pOpts->GetCDOpts();
                    pCDOpts->dwBatchedTitles = GetNumBatchedTitles();
                    pOpts->DownLoadCompletion(1,&(pSingleTitle->dwTitleID));
                } //end if OK to update batch number
            }
        }

        return 0;
    }
    
    return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * OptionsApply
// Called from the options dialog when appy is hit, or by main UI when OK is hit
////////////////////////////////////////////////////////////////////////////////////////////
void CALLBACK OptionsApply(LPCDOPTIONS pCDOpts)
{
    if (!pCDOpts)
    {
        return;
    }
    
    //tell the CD player to rescan its settings ... 
    //most of the settings are for it
	IMMComponentAutomation* pAuto = NULL;
	HRESULT hr = pNodeCurrent->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&pAuto);
	if ((SUCCEEDED(hr)) && (pAuto != NULL))
	{
		pAuto->OnAction(MMACTION_READSETTINGS,pCDOpts->pCDData);
        pAuto->Release();
    }

    LPCDUNIT pUnit = pCDOpts->pCDUnitList;
    LPCDDATA pData = GetCDData();

    while (pUnit!=NULL)
    {
        BOOL fRemove = FALSE;
        if (pData)
        {
            fRemove = !(pData->QueryTitle(pUnit->dwTitleID));
        }

        if ((pUnit->fChanged) || (fRemove))
        {
            if ((pUnit->dwTitleID != 0) && (pUnit->dwTitleID != (DWORD)-1))
            {
                //tell the cd player that a title was updated, perhaps the ones in the drive
                MMNET mmNet;
                mmNet.discid = pUnit->dwTitleID;
                mmNet.hwndCallback = hwndMain;
                mmNet.pData = (void*)GetCDData();
                mmNet.pData2 = NULL;
                mmNet.fForceNet = FALSE;
                SendMessage(hwndCurrentComp,WM_COMMAND,MAKEWPARAM(ID_CDUPDATE,0),(LPARAM)&mmNet);
            }
        }
        pUnit = pUnit->pNext;
    }

    SetWindowPos(hwndMain,
                 pCDOpts->pCDData->fTopMost ? HWND_TOPMOST : HWND_NOTOPMOST,
                 0,0,0,0,
                 SWP_NOMOVE|SWP_NOSIZE);

    //may have changed cd volume line
    if (InitCDVol(hwndMain, pCDOpts))
    {
        //when the volume line changes, we need to update the knob
        DWORD dwVol = GetVolume();
        CKnob* pKnob = GetKnobFromID(hwndMain,IDB_VOLUME);
        if (pKnob!=NULL)
        {
            pKnob->SetPosition(dwVol,TRUE);
        }

        SendMessage(GetDlgItem(hwndMain,IDB_MUTE),BM_SETSTATE,(WPARAM)GetMute(),0);
        SendMessage(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),WM_LED_MUTE,0,GetMute());
    } //if vol line changed

    //may have turned on/off shell mode
    //we only care if the main UI is visible (i.e. not in shell-only mode)
    if (IsWindowVisible(hwndMain))
    {
        if (fShellMode != pCDOpts->pCDData->fTrayEnabled)
        {
            fShellMode = pCDOpts->pCDData->fTrayEnabled;
            if (fShellMode)
            {
                CreateShellIcon(hInst,hwndMain,pNodeCurrent,szAppName);
            }
            else
            {
                DestroyShellIcon();
            } //end shellmode
        } //end if shellmode changed in options
    } //end main ui visible
}

////////////////////////////////////////////////////////////////////////////////////////////
// * HandleMixerControlChange
// Updates UI when sndvol32 or other apps change our mixerline
////////////////////////////////////////////////////////////////////////////////////////////
void HandleMixerControlChange(DWORD dwLineID)
{
    if (dwLineID == mixerlinedetails.dwControlID)
    {
        DWORD dwVol = GetVolume();
	    CKnob* pKnob = GetKnobFromID(hwndMain,IDB_VOLUME);
        //possible to get this change before knob is up
        if (pKnob!=NULL)
        {
            pKnob->SetPosition(dwVol,TRUE);
        }
    }

    if (dwLineID == mutelinedetails.dwControlID)
    {
        SendMessage(GetDlgItem(hwndMain,IDB_MUTE),BM_SETSTATE,(WPARAM)GetMute(),0);
        SendMessage(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),WM_LED_MUTE,0,GetMute());
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * ChildPaletteProc
// Updates child windows when palette changes
////////////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK ChildPaletteProc(HWND hwnd, LPARAM lParam)
{
    InvalidateRect(hwnd,NULL,FALSE);
    UpdateWindow(hwnd);
    return TRUE;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * HandlePaletteChange
// Updates window when palette changes
////////////////////////////////////////////////////////////////////////////////////////////
int HandlePaletteChange()
{
    HDC hdc = GetDC(hwndMain);
    HPALETTE hOldPal = SelectPalette(hdc,hpalMain,FALSE);
    UINT i = RealizePalette(hdc);
    if (i)
    {
        //update child windows "by hand", since they are clipped
        EnumChildWindows(hwndMain,ChildPaletteProc,0);

        //update main window
        InvalidateRect(hwndMain,NULL,FALSE);
        UpdateWindow(hwndMain);
    }
    SelectPalette(hdc,hOldPal,TRUE);
    RealizePalette(hdc);
    ReleaseDC(hwndMain,hdc);
    return i;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * HandleDisplayChange
// Figures out if we need to recalc all bitmaps and does so ...
////////////////////////////////////////////////////////////////////////////////////////////
void HandleDisplayChange()
{
    int nOrgColorMode = g_nColorMode;

    DetermineColorMode();

    //only do anythhing if they changed modes
    if (g_nColorMode != nOrgColorMode)
    {
        //nuke all child windows
        for (int i = 0; i < NUM_BUTTONS; i++)
        {
            DestroyWindow(GetDlgItem(hwndMain,biButtons[i].id));
        }

        DestroyWindow(GetDlgItem(hwndMain,IDB_MUTE));
        DestroyWindow(GetDlgItem(hwndMain,IDB_VOLUME));
	    UninitMButtons();

        //nuke the bitmaps
	    GlobalFree(hbmpMain);
	    GlobalFree(hbmpMainRestore);
        GlobalFree(hbmpMainSmall);
        GlobalFree(hbmpMainNoBar);
	    DeleteObject(hpalMain);

    	hpalMain = SetPalette();

        //rebuild the bitmaps
        BuildFrameworkBitmaps();

        //recreate the windows
	    CreateButtonWindows(hwndMain);
	    CreateVolumeKnob(hwndMain);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * DoPaint
// Handles the WM_PAINT for the main UI, actually a multimon paint callback
////////////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK DoPaint(HMONITOR hmonitor, HDC hdc, LPRECT lprcMonitor, LPARAM lData)
{
	HWND hwnd = (HWND)lData;

    HPALETTE hpalOld = SelectPalette(hdc, hpalMain, FALSE);
	RealizePalette(hdc);

	BOOL fActiveWindow = FALSE;
    if (hwnd == GetForegroundWindow())
    {
	    fActiveWindow = TRUE;
    }

	DrawTitleBar(hdc, hwnd, fActiveWindow, TRUE);

    HANDLE hbmpView;
    BITMAP* pBM = &bmMain;
    switch (g_nViewMode)
    {
        case VIEW_MODE_NORMAL  :
        {
            hbmpView = hbmpMain; 
        }
        break;
    
        case VIEW_MODE_RESTORE :
        {
            hbmpView = hbmpMainRestore; 
            pBM = &bmMainRestore;
        }
        break;

        case VIEW_MODE_SMALL :
        {
            hbmpView = hbmpMainSmall;
            pBM = &bmMainSmall;
        }
        break;

        case VIEW_MODE_NOBAR :
        {
            hbmpView = hbmpMainNoBar;
            pBM = &bmMainNoBar;
        }
        break;
    }

    DibBlt(hdc,
        0,
        0,
        -1, 
        -1, 
        hbmpView,
        0,0,
        SRCCOPY,0);

    //reset the clipping region that was set in DrawTitleBar for reverse paint
    RECT rcParent;
    GetClientRect( hwnd, &rcParent );
    
    HRGN region = CreateRectRgn(rcParent.left,rcParent.top,rcParent.right,rcParent.bottom);

    SelectClipRgn(hdc, region);
    DeleteObject(region);

	SelectPalette(hdc, hpalOld, TRUE);
	RealizePalette(hdc);

    return TRUE;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * SetPlayButtonState
// Handles the play button's icon
////////////////////////////////////////////////////////////////////////////////////////////
void SetPlayButtonState(BOOL fIntroMode)
{
    CMButton* pButton = GetMButtonFromID(hwndMain,IDB_PLAY);

    if (pButton)
    {
        if ((fPlaying) && (fIntroMode))
        {
            pButton->SetIcon(IDI_MODE_INTRO);
            pButton->SetToolTipID(IDB_TT_INTRO);
        }

        if ((fPlaying) && (fIntro) && (!fIntroMode))
        {
            pButton->SetIcon(IDI_ICON_PAUSE);
            pButton->SetToolTipID(IDB_TT_PAUSE);
        }
    }

    fIntro = fIntroMode;

    //need to save playback mode at this point
    LPCDOPT pOpt = GetCDOpt();

    if(pOpt)
    {
        LPCDOPTIONS pOptions = pOpt->GetCDOpts();
        LPCDOPTDATA pOptionData = pOptions->pCDData;
        pOptionData->dwPlayMode = nCDMode;
        pOpt->UpdateRegistry();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * GetSearchURL
// Returns the standard IE "autosearch" URL
////////////////////////////////////////////////////////////////////////////////////////////
BOOL GetSearchURL(TCHAR* szSearchURL)
{
    BOOL fRet = FALSE;
    HKEY hKeySearch = NULL;

	long lResult = ::RegOpenKeyEx( HKEY_CURRENT_USER,
							  REG_KEY_SEARCHURL,
							  0, KEY_READ, &hKeySearch );

    if (lResult == ERROR_SUCCESS)
    {
        DWORD dwCbData = MAX_PATH;
        DWORD dwType = REG_SZ;
        lResult  = ::RegQueryValueEx( hKeySearch, TEXT(""), NULL,
						          &dwType, (LPBYTE)szSearchURL, &dwCbData );

        if (lResult == ERROR_SUCCESS)
        {
            fRet = TRUE;
        }

        if ((_tcslen(szSearchURL) < 1) || (lResult != ERROR_SUCCESS))
        {
            _tcscpy(szSearchURL,TEXT("http://home.microsoft.com/access/autosearch.asp?p=%s"));
            fRet = TRUE;
        }

        RegCloseKey(hKeySearch);
    }

    return (fRet);
}

////////////////////////////////////////////////////////////////////////////////////////////
// * HandleCommand
// Handles all WM_COMMAND message for the main app
////////////////////////////////////////////////////////////////////////////////////////////
void HandleCommand(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    //handle system commands
    if ((LOWORD(wParam) >= SC_SIZE) && (LOWORD(wParam) <= SC_CONTEXTHELP))
    {
        SendMessage(hwnd,WM_SYSCOMMAND,(WPARAM)LOWORD(wParam),0);
        return;
    }

	switch (LOWORD(wParam))
	{
		case IDM_EXIT :
		{
            //if the shell icon is showing, then closing the app means hiding it
		    if (fShellMode)
            {
                ShowWindow(hwnd,SW_HIDE);
            }
            else
            {
                SendMessage(hwnd, WM_CLOSE, 0, 0);
            }
		    return;
		} //end exit
        break;

        case IDM_EXIT_SHELL :
        {
            //if the shell icon wanted us to shut down, but the main ui is visible, just
            //nuke shell mode
            if (IsWindowVisible(hwnd))
            {
                DestroyShellIcon();
                fShellMode = FALSE;
            }
            else
            {
                SendMessage(hwnd, WM_CLOSE, 0, 0);
            }
            return;
        }
        break;
	}

	IMMComponentAutomation* pAuto = NULL;
	HRESULT hr = pNodeCurrent->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&pAuto);
	if ((SUCCEEDED(hr)) && (pAuto != NULL))
	{
        if ((LOWORD(wParam) >= IDM_TRACKLIST_BASE) && (LOWORD(wParam) < IDM_TRACKLIST_SHELL_BASE))
        {
            MMCHANGETRACK mmTrack;
            mmTrack.nNewTrack = LOWORD(wParam) - IDM_TRACKLIST_BASE;
            pAuto->OnAction(MMACTION_SETTRACK,&mmTrack);
        }

        if ((LOWORD(wParam) >= IDM_TRACKLIST_SHELL_BASE) && (LOWORD(wParam) < IDM_DISCLIST_BASE))
        {
            MMCHANGETRACK mmTrack;
            mmTrack.nNewTrack = LOWORD(wParam) - IDM_TRACKLIST_SHELL_BASE;
            pAuto->OnAction(MMACTION_SETTRACK,&mmTrack);

            //play it if we're not playing
            if (!fPlaying)
            {
                pAuto->OnAction(MMACTION_PLAY,NULL);
            }
        }

        if ((LOWORD(wParam) >= IDM_DISCLIST_BASE) && (LOWORD(wParam) < IDM_NET_UPDATE))
        {
            MMCHANGEDISC mmDisc;
            mmDisc.nNewDisc = LOWORD(wParam) - IDM_DISCLIST_BASE;
            pAuto->OnAction(MMACTION_SETDISC,&mmDisc);
        }

        if ((LOWORD(wParam) >= IDM_HOMEMENU_BASE) && (LOWORD(wParam) < IDM_NETMENU_BASE))
        {
            int i = LOWORD(wParam) - IDM_HOMEMENU_BASE;

            LPCDOPT pOpt = GetCDOpt();
            if( pOpt )
            {
                LPCDOPTIONS pCDOpts = pOpt->GetCDOpts();

                LPCDPROVIDER pProviderList = pCDOpts->pProviderList;

                for (int x = 0; x < i; x++)
                {
                    pProviderList = pProviderList->pNext;
                }

                OpenBrowserURL(pProviderList->szProviderHome);
            }
        }

        if ((LOWORD(wParam) >= IDM_NETMENU_BASE) && (LOWORD(wParam) < IDM_OPTIONS))
        {
            MMMEDIAID mmMedia;
            mmMedia.nDrive = -1;
	        pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);
    
            LPCDDATA pData = GetCDData();

            if (!pData)
            {
                return;
            }

            if (!pData->QueryTitle(mmMedia.dwMediaID))
            {
                return;
            }

            LPCDTITLE pCDTitle = NULL;
            hr =  pData->LockTitle(&pCDTitle,mmMedia.dwMediaID);

            if (FAILED(hr))
            {
                return;
            }

            LPCDMENU pCDMenu = &(pCDTitle->pMenuTable[LOWORD(wParam)-IDM_NETMENU_BASE]);

            if (pCDMenu->szMenuQuery)
            {
                OpenBrowserURL(pCDMenu->szMenuQuery);
            }

            pData->UnlockTitle(pCDTitle,FALSE);
        }

        switch (LOWORD(wParam))
        {
            case IDM_NET_UPDATE :
            {
                MMNET mmNet;
                mmNet.discid = 0; //use current disc
                mmNet.hwndCallback = hwnd;
                mmNet.pData = (void*)GetCDData();
                mmNet.pData2 = NULL;
                mmNet.fForceNet = FALSE;
                SendMessage(hwndCurrentComp,WM_COMMAND,MAKEWPARAM(ID_CDUPDATE,0),(LPARAM)&mmNet);
            }
            break;

            case IDM_NET_CANCEL :
            {
                CancelDownload();
            }
            break;

            case IDM_NET_ROLLINGSTONE_ARTIST :
            case IDM_NET_BILLBOARD_ALBUM :
            case IDM_NET_BILLBOARD_ARTIST :
            {            
	            MMMEDIAID mmMedia;
                mmMedia.nDrive = -1;
	            pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);
    
                TCHAR szQuery[MAX_PATH*4];

                LPCDDATA pData = GetCDData();

                if (!pData)
                {
	                break;
                }

                if (!pData->QueryTitle(mmMedia.dwMediaID))
                {
                    return;
                }

                LPCDTITLE pCDTitle = NULL;
                hr =  pData->LockTitle(&pCDTitle,mmMedia.dwMediaID);

                if (FAILED(hr))
                {
                    break;
                }

                //normalize (canonize) the title and artist names
                TCHAR szArtist[CDSTR*3];
                TCHAR szTitle[CDSTR*3];
                NormalizeNameForURL(pCDTitle->szArtist,szArtist,sizeof(szArtist));
                NormalizeNameForURL(pCDTitle->szTitle,szTitle,sizeof(szTitle));

                pData->UnlockTitle(pCDTitle,FALSE);

                if ((LOWORD(wParam) == IDM_NET_BILLBOARD_ARTIST))
                {
                    TCHAR szFormat[MAX_PATH];
                    LoadString(hInst,IDS_BILLBOARD_FORMAT_ARTIST,szFormat,sizeof(szFormat)/sizeof(TCHAR));
                    wsprintf(szQuery,szFormat,szArtist);
                }
                else if ((LOWORD(wParam) == IDM_NET_ROLLINGSTONE_ARTIST))
                {
                    TCHAR szFormat[MAX_PATH];
                    LoadString(hInst,IDS_ROLLINGSTONE_FORMAT_ARTIST,szFormat,sizeof(szFormat)/sizeof(TCHAR));
                    wsprintf(szQuery,szFormat,szArtist);
                }
                else
                {
                    TCHAR szFormat[MAX_PATH];
                    LoadString(hInst,IDS_BILLBOARD_FORMAT_ALBUM,szFormat,sizeof(szFormat)/sizeof(TCHAR));
                    wsprintf(szQuery,szFormat,szArtist,szTitle);
                }

                OpenBrowserURL(szQuery);
            }
            break;

            case IDM_NET_CD :
            {
	            MMMEDIAID mmMedia;
                mmMedia.nDrive = -1;
	            pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);
    
                TCHAR szQuery[MAX_PATH*4];

                LPCDDATA pData = GetCDData();

                if (!pData)
                {
	                break;
                }

                if (!pData->QueryTitle(mmMedia.dwMediaID))
                {
                    return;
                }

                LPCDTITLE pCDTitle = NULL;
                hr =  pData->LockTitle(&pCDTitle,mmMedia.dwMediaID);

                if (FAILED(hr))
                {
                    break;
                }

                TCHAR szSearchURL[MAX_PATH];
                if (!GetSearchURL(szSearchURL))
                {
                    break;
                }

                TCHAR szTemp[MAX_PATH*2];
                TCHAR szTemp2[MAX_PATH*2];

                wsprintf(szTemp,TEXT("%s %s"),pCDTitle->szArtist,pCDTitle->szTitle);
                NormalizeNameForURL(szTemp,szTemp2,sizeof(szTemp2));
                wsprintf(szQuery,szSearchURL,szTemp2);

                pData->UnlockTitle(pCDTitle,FALSE);

                OpenBrowserURL(szQuery);
            }
            break;

            case IDM_NET_BAND :
            {
	            MMMEDIAID mmMedia;
                mmMedia.nDrive = -1;
	            pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);
    
                TCHAR szQuery[MAX_PATH*3];

                LPCDDATA pData = GetCDData();

                if (!pData)
                {
	                break;
                }

                if (!pData->QueryTitle(mmMedia.dwMediaID))
                {
                    return;
                }

                LPCDTITLE pCDTitle = NULL;
                hr =  pData->LockTitle(&pCDTitle,mmMedia.dwMediaID);

                if (FAILED(hr))
                {
                    break;
                }

                TCHAR szSearchURL[MAX_PATH];
                if (!GetSearchURL(szSearchURL))
                {
                    break;
                }

                TCHAR szArtist[CDSTR*3];
                NormalizeNameForURL(pCDTitle->szArtist,szArtist,sizeof(szArtist));

                wsprintf(szQuery,szSearchURL,szArtist);

                pData->UnlockTitle(pCDTitle,FALSE);

                OpenBrowserURL(szQuery);
            }
            break;

            case IDM_MODE_NORMAL :
            {
                nCDMode = LOWORD(wParam);
                SendMessage(hwndCurrentComp,WM_COMMAND,MAKEWPARAM(IDM_OPTIONS_NORMAL,0),(LPARAM)0);
                InvalidateRect(hwndCurrentComp,NULL,FALSE);
                UpdateWindow(hwndCurrentComp);
	            CMButton* pButton = GetMButtonFromID(hwnd,IDB_MODE);
                if (pButton)
                {
	                pButton->SetIcon(IDI_MODE_NORMAL);
                }
                SetPlayButtonState(FALSE);
            }
            break;

            case IDM_MODE_REPEATONE :
            {
                nCDMode = LOWORD(wParam);
                SendMessage(hwndCurrentComp,WM_COMMAND,MAKEWPARAM(IDM_OPTIONS_REPEAT_SINGLE,0),(LPARAM)0);
                InvalidateRect(hwndCurrentComp,NULL,FALSE);
                UpdateWindow(hwndCurrentComp);
	            CMButton* pButton = GetMButtonFromID(hwnd,IDB_MODE);
                if (pButton)
                {
	                pButton->SetIcon(IDI_MODE_REPEATONE);
                }
                SetPlayButtonState(FALSE);
            }
            break;

            case IDM_MODE_REPEATALL :
            {
                nCDMode = LOWORD(wParam);
                SendMessage(hwndCurrentComp,WM_COMMAND,MAKEWPARAM(IDM_OPTIONS_CONTINUOUS,0),(LPARAM)0);
                InvalidateRect(hwndCurrentComp,NULL,FALSE);
                UpdateWindow(hwndCurrentComp);
	            CMButton* pButton = GetMButtonFromID(hwnd,IDB_MODE);
                if (pButton)
                {
	                pButton->SetIcon(IDI_MODE_REPEATALL);
                }
                SetPlayButtonState(FALSE);
            }
            break;

            case IDM_MODE_RANDOM :
            {
                nCDMode = LOWORD(wParam);
                SendMessage(hwndCurrentComp,WM_COMMAND,MAKEWPARAM(IDM_OPTIONS_RANDOM,0),(LPARAM)0);
                InvalidateRect(hwndCurrentComp,NULL,FALSE);
                UpdateWindow(hwndCurrentComp);
	            CMButton* pButton = GetMButtonFromID(hwnd,IDB_MODE);
                if (pButton)
                {
    	            pButton->SetIcon(IDI_MODE_RANDOM);
                }
                SetPlayButtonState(FALSE);
            }
            break;

            case IDM_MODE_INTRO :
            {
                nCDMode = LOWORD(wParam);
                SendMessage(hwndCurrentComp,WM_COMMAND,MAKEWPARAM(IDM_OPTIONS_INTRO,0),(LPARAM)0);
                InvalidateRect(hwndCurrentComp,NULL,FALSE);
                UpdateWindow(hwndCurrentComp);
	            CMButton* pButton = GetMButtonFromID(hwnd,IDB_MODE);
                if (pButton)
                {
	                pButton->SetIcon(IDI_MODE_INTRO);
                }
                SetPlayButtonState(TRUE);
            }
            break;

            case IDM_HELP :
            {
                char chDst[MAX_PATH];
                WideCharToMultiByte(CP_ACP, 0, HELPFILENAME, 
									            -1, chDst, MAX_PATH, NULL, NULL); 
	            HtmlHelpA(GetDesktopWindow(), chDst, HH_DISPLAY_TOPIC, 0);
            }
            break;

            case IDM_ABOUT :
            {
                ShellAbout( hwnd, szAppName, TEXT(""), LoadIcon(hInst, MAKEINTRESOURCE(IDI_MMFW)));
            }
            break;

            case IDM_NORMAL :
            {
                SetNormalMode(hwnd);
            }
            break;

            case IDM_TINY :
            {
                SetRestoredMode(hwnd);
            }
            break;

            case IDB_MUTE :
            {
                //fix for bug 220 ... if mute button is set from "SetMute" during a button
                //click, it will get ANOTHER button click when it's focus is killed, so we
                //need to make sure it stays in the right state at the right time
                if (GetFocus()==GetDlgItem(hwndMain,IDB_MUTE))
                {
                    SetMute();
                }
                else
                {
                    SendMessage(GetDlgItem(hwndMain,IDB_MUTE),BM_SETSTATE,(WPARAM)GetMute(),0);
                }
            }
            break;

            case IDM_PLAYLIST :         
            case IDM_OPTIONS :
            {
                CDOPT_PAGE  nStartSheet = CDOPT_PAGE_PLAY;

                if (LOWORD(wParam) == IDM_PLAYLIST)
                {
                    nStartSheet = CDOPT_PAGE_PLAYLIST;
                }

                LPCDOPT pOpt = GetCDOpt();
                if( pOpt )
                {
                    LPCDOPTIONS pCDOpts = pOpt->GetCDOpts();

                    LPCDDATA pData = GetCDData();

                    if( pData )
                    {
                        //go through and set media ids for each drive
                        LPCDUNIT pUnit = pCDOpts->pCDUnitList;
                        int nCurrDrive = 0;
    
                        while (pUnit!=NULL)
                        {
			                MMMEDIAID mmMedia;
                            mmMedia.nDrive = nCurrDrive;
			                pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);

                            MMNETQUERY mmNetQuery;
                            mmNetQuery.nDrive = nCurrDrive++;
                            mmNetQuery.szNetQuery = pUnit->szNetQuery;
                            pAuto->OnAction(MMACTION_GETNETQUERY,&mmNetQuery);

                            pUnit->dwTitleID = mmMedia.dwMediaID;
                            pUnit->dwNumTracks = mmMedia.dwNumTracks;
                            if (IsDownloading())
                            {
                                pUnit->fDownLoading = TRUE;
                            }
                            else
                            {
                                pUnit->fDownLoading = FALSE;
                            }
                            pUnit = pUnit->pNext;
                        }

                        //set the number of batched titles and the callback functions
                        pCDOpts->dwBatchedTitles = GetNumBatchedTitles();
                        pCDOpts->pfnDownloadTitle = OptionsDownloadCallback;
                        pCDOpts->pfnOptionsCallback = OptionsApply;

                        fOptionsDlgUp = TRUE;
                        HRESULT hr = pOpt->OptionsDialog(hwnd, pData, nStartSheet);
                        fOptionsDlgUp = FALSE;

                        if (hr == S_OK) //don't use succeeded macro here, S_FALSE is also valid
                        {
                            OptionsApply(pCDOpts);
                        }
                    } //if pdata
                }
            }
            break;

            case IDB_PLAY :
            {
	            if (fPlaying)
	            {
                    if (fIntro)
                    {
                        //set to normal mode
                        SendMessage(hwnd,WM_COMMAND,MAKEWPARAM(IDM_MODE_NORMAL,0),0);
                        //set button back to regular icon
                        CMButton* pButton = GetMButtonFromID(hwnd,IDB_PLAY);
                        if (pButton)
                        {
                            pButton->SetIcon(IDI_ICON_PAUSE);
                            pButton->SetToolTipID(IDB_TT_PAUSE);
                        }
                    }
                    else
                    {
		                pAuto->OnAction(MMACTION_PAUSE,NULL);
                    }
	            }
	            else
	            {
		            pAuto->OnAction(MMACTION_PLAY,NULL);
	            }
            }
            break;

            case IDB_EJECT :
            {
	            pAuto->OnAction(MMACTION_UNLOADMEDIA,NULL);
            }
            break;

            case IDB_FFWD :
            {
	            pAuto->OnAction(MMACTION_FFWD,NULL);
            }
            break;

            case IDB_NEXTTRACK :
            {
                pAuto->OnAction(MMACTION_NEXTTRACK,NULL);
            }
            break;

            case IDB_PREVTRACK :
            {
	            pAuto->OnAction(MMACTION_PREVTRACK,NULL);
            }
            break;

            case IDB_REW :
            {
	            pAuto->OnAction(MMACTION_REWIND,NULL);
            }
            break;

            case IDB_STOP :
            {
	            pAuto->OnAction(MMACTION_STOP,NULL);
            }
            break;

            case IDB_MINIMIZE :
            {
                ShowWindow(hwnd,SW_MINIMIZE);
                //see \\redrum\slmro\proj\win\src\CORE\user\mssyscmd.c
                PlaySound(TEXT("Minimize"),NULL,SND_ALIAS|SND_NODEFAULT|SND_ASYNC|SND_NOWAIT|SND_NOSTOP);
            }
            break;

            case IDB_SET_NORMAL_MODE :
            {
                SetNormalMode(hwnd);
            }
            break;

            case IDB_SET_TINY_MODE :
            {
                SetRestoredMode(hwnd);
            }
            break;

            case IDB_CLOSE :
            {
                //if the shell icon is showing, then closing the app means hiding it
		        if (fShellMode)
                {
                    ShowWindow(hwnd,SW_HIDE);
                }
                else
                {
                    SendMessage(hwnd, WM_CLOSE, 0, 0);
                }
            }
            break;
        }

        if (pAuto)
        {
            pAuto->Release();
	        pAuto = NULL;
        }
	}
}

////////////////////////
// Handles device change message from WinMM, All we need to do here is close any open
// Mixer Handles, re-compute mixer ID and control ID's and re-Open appropriately
//
void WinMMDeviceChangeHandler(HWND hWnd)
{
    LPCDOPT pOpt = GetCDOpt();

    if (hmix)                           // Close open mixer handle
    {
        mixerClose((HMIXER)hmix);
        hmix = NULL;
    }

    if(pOpt)
    {
        LPCDOPTIONS pCDOpts = pOpt->GetCDOpts();

        pOpt->MMDeviceChanged();

        InitCDVol(hwndMain,pCDOpts);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * HandlePowerBroadcast
// On VxD drivers, you can't "suspend" with an open mixer device.  Bug 1132.
//
// If lParam == 1, this is a device remove
//
////////////////////////////////////////////////////////////////////////////////////////////
BOOL HandlePowerBroadcast(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    BOOL fRet = TRUE;
    
    switch (wParam)
    {
        case PBT_APMQUERYSTANDBY:
	    case PBT_APMQUERYSUSPEND:
        {
            if (hmix)
	        {
    		    mixerClose((HMIXER)hmix);
                hmix = NULL;
	        }
        } //end case power off
	    break;

        case PBT_APMSTANDBY :
        case PBT_APMSUSPEND :
        {
            //actually suspending, go ahead and stop the cd
            if (fPlaying)
            {
	            IMMComponentAutomation* pAuto = NULL;
                if (pNodeCurrent)
                {
	                HRESULT hr = pNodeCurrent->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&pAuto);
	                if ((SUCCEEDED(hr)) && (pAuto != NULL))
	                {
                        //at this point, we can actually stop the CD
                        pAuto->OnAction(MMACTION_STOP,NULL);        
                        //release the automation object        
                        pAuto->Release();
                    }
                } //end if pnodecurrent
            } //end if playing
        }
        break;

	    case PBT_APMQUERYSTANDBYFAILED:
        case PBT_APMRESUMESTANDBY:
        case PBT_APMQUERYSUSPENDFAILED:
	    case PBT_APMRESUMESUSPEND:
        {
            WinMMDeviceChangeHandler(hWnd);
        } //end case power on
	    break;
    } //end switch

    return fRet;
}


////////////////////////////////////////////////////////////////////////////////////////////
// * HandleSysMenuInit
// Make sure the system menu only shows the correct choices
////////////////////////////////////////////////////////////////////////////////////////////
void HandleSysMenuInit(HWND hwnd, HMENU hmenu)
{
    //always gray out
    EnableMenuItem(hmenu,SC_SIZE,    MF_BYCOMMAND|MF_GRAYED);
    EnableMenuItem(hmenu,SC_MAXIMIZE,MF_BYCOMMAND|MF_GRAYED);

    //always enable
    EnableMenuItem(hmenu,SC_CLOSE,MF_BYCOMMAND|MF_ENABLED);

    //enable or gray based on minimize state
    if (IsIconic(hwnd))
    {
        EnableMenuItem(hmenu,SC_RESTORE, MF_BYCOMMAND|MF_ENABLED);
        EnableMenuItem(hmenu,SC_MOVE,    MF_BYCOMMAND|MF_GRAYED);
        EnableMenuItem(hmenu,SC_MINIMIZE,MF_BYCOMMAND|MF_GRAYED);
    }
    else
    {
        EnableMenuItem(hmenu,SC_RESTORE, MF_BYCOMMAND|MF_GRAYED);
        EnableMenuItem(hmenu,SC_MOVE,    MF_BYCOMMAND|MF_ENABLED);
        EnableMenuItem(hmenu,SC_MINIMIZE,MF_BYCOMMAND|MF_ENABLED);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////
// * SysMenuTimerProc
// Make sure the system menu only shows on a single click
////////////////////////////////////////////////////////////////////////////////////////////
void CALLBACK SysMenuTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
    KillTimer(hwnd,idEvent);

    POINTS pts;
    RECT rect;

    //make sure the menu shows up in a good-looking place
    GetClientRect(hwnd,&rect);
    rect.left = SYSMENU_XOFFSET;
    rect.right = SYSMENU_XOFFSET + SYSMENU_WIDTH;
    rect.bottom = rect.top + TITLEBAR_HEIGHT +
                    (g_nViewMode == VIEW_MODE_NORMAL ? TITLEBAR_YOFFSET_LARGE :
                                                       TITLEBAR_YOFFSET_SMALL);

    ClientToScreen(hwnd,(LPPOINT)&rect);
    ClientToScreen(hwnd,((LPPOINT)&rect)+1);
    pts.x = (short)rect.left;
    pts.y = (short)rect.bottom;

    HMENU hSysMenu = GetSystemMenu(hwnd,FALSE);

    TPMPARAMS tpm;
    tpm.cbSize = sizeof(tpm);
    memcpy(&(tpm.rcExclude),&rect,sizeof(RECT));
    TrackPopupMenuEx(hSysMenu,0,pts.x,pts.y,hwnd,&tpm);
}



BOOL HandleKeyboardAppCommand(HWND hwnd, short cmd)
{
    BOOL fHandled = FALSE;

    switch (cmd)
    {
        case APPCOMMAND_VOLUME_MUTE :
        {
            SetMute();
            fHandled = TRUE;
        }
        break;

        case APPCOMMAND_VOLUME_DOWN :
        {
            SendMessage(GetDlgItem(hwnd,IDB_VOLUME),WM_KEYDOWN,VK_DOWN,0);
            fHandled = TRUE;
        }
        break;

        case APPCOMMAND_VOLUME_UP :
        {
            SendMessage(GetDlgItem(hwnd,IDB_VOLUME),WM_KEYDOWN,VK_UP,0);
            fHandled = TRUE;
        }
        break;

        case APPCOMMAND_MEDIA_NEXTTRACK :
        {
            SendMessage(hwnd,WM_COMMAND,MAKEWPARAM(IDB_NEXTTRACK,0),(LPARAM)0);
            fHandled = TRUE;
        }
        break;

        case APPCOMMAND_MEDIA_PREVIOUSTRACK :
        {
            SendMessage(hwnd,WM_COMMAND,MAKEWPARAM(IDB_PREVTRACK,0),(LPARAM)0);
            fHandled = TRUE;
        }
        break;

        case APPCOMMAND_MEDIA_STOP :
        {
            SendMessage(hwnd,WM_COMMAND,MAKEWPARAM(IDB_STOP,0),(LPARAM)0);
            fHandled = TRUE;
        }
        break;

        case APPCOMMAND_MEDIA_PLAY_PAUSE :
        {
            SendMessage(hwnd,WM_COMMAND,MAKEWPARAM(IDB_PLAY,0),(LPARAM)0);
            fHandled = TRUE;
        }
        break;

        default:
        {
            fHandled = FALSE;
        }
        break;
    } //end switch

    return fHandled;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * MainWndProc
// Main window's message switcher
////////////////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    switch (iMsg)
    {
	    //we're being created, start up
        case WM_CREATE :
	    {
            hwndMain = hwnd;

            g_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
            giVolDevChange    = RegisterWindowMessage(TEXT("winmm_devicechange"));

            CreateToolTips(hwnd);
	        InitComponents(hwnd);
	        CreateButtonWindows(hwnd);
	        CreateVolumeKnob(hwnd);

            //if no disc in player, gray out the track button
	        IMMComponentAutomation* pAuto = NULL;
	        HRESULT hr = pNodeCurrent->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&pAuto);
	        if ((SUCCEEDED(hr)) && (pAuto != NULL))
	        {
                MMMEDIAID mmMedia;
                mmMedia.nDrive = -1;
                pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);
                pAuto->Release();
                if (mmMedia.dwMediaID == 0)
                {
                    EnableWindow(GetDlgItem(hwnd,IDB_TRACK),FALSE);
                }
            }
	    }
        break;

        case SHELLMESSAGE_CDICON :
        {
            return (ShellIconHandeMessage(lParam));
        }
        break;

        //called from sndvol32 or other apps that change mixer
        case MM_MIXM_CONTROL_CHANGE :
        {
            HandleMixerControlChange((DWORD)lParam);
        }
        break;

        //palette changed
        case WM_PALETTECHANGED :
        case WM_QUERYNEWPALETTE :
	    {
            return (HandlePaletteChange());
	    }
	    break;

        //autoplay copied command line when second instance running
        case WM_COPYDATA :
        {
            SendMessage(hwndCurrentComp,WM_COPYDATA,wParam,lParam);
        }
        break;

        //new keyboard interface
        case WM_APPCOMMAND :
        {
            BOOL fHandled = HandleKeyboardAppCommand(hwnd,GET_APPCOMMAND_LPARAM(lParam));

            if (fHandled)
            {
                return fHandled;
            }
            
            //otherwise will go to defwindowproc
        }
        break;
	    
	    //activation/deactivation -- need to repaint title
        case WM_ACTIVATE :
        {
            HDC hdc = GetDC(hwnd);
            DrawTitleBar(hdc,hwnd,LOWORD(wParam),FALSE);
	        ReleaseDC(hwnd,hdc);
        }
        break;

        //check for switch to tiny mode
        case WM_NCLBUTTONDBLCLK :
        case WM_LBUTTONDBLCLK :
        {
            KillTimer(hwnd,SYSTIMERID);

            if (((int)wParam == HTSYSMENU) && (iMsg == WM_NCLBUTTONDBLCLK))
            {
                break; //don't allow this on a sys menu double-click
            }
            
            switch (g_nViewMode)
            {
                case VIEW_MODE_NORMAL  : SetNoBarMode(hwnd);    break;
                case VIEW_MODE_NOBAR   : SetNormalMode(hwnd);   break;
                case VIEW_MODE_RESTORE : SetSmallMode(hwnd);    break;
                case VIEW_MODE_SMALL   : SetRestoredMode(hwnd); break;
            } //end switch on view mode
        }
        break;

        //handle left click on system menu
        //need to set timer to handle double-click
        case WM_NCLBUTTONDOWN :
        {
            if ((int)wParam == HTSYSMENU)
            {
                SetTimer(hwnd,SYSTIMERID,GetDoubleClickTime()+100,(TIMERPROC)SysMenuTimerProc);
            }
        }
        break;

        //handle right click on system menu or caption
        //no need for timer on double-click
        case WM_NCRBUTTONDOWN :
        {
            if (((int)wParam == HTCAPTION) || ((int)wParam == HTSYSMENU))
            {
                POINTS pts = MAKEPOINTS(lParam);
                HMENU hSysMenu = GetSystemMenu(hwnd,FALSE);
                TrackPopupMenu(hSysMenu,0,pts.x,pts.y,0,hwnd,NULL);
            }
        }
        break;

        case WM_INITMENU :
        {
            HandleSysMenuInit(hwnd,(HMENU)wParam);
        }
        break;

        case WM_POWERBROADCAST:
        {
            return (HandlePowerBroadcast(hwnd,wParam,0));
        }
        break;

	    //check for mouse in title bar
        case WM_NCHITTEST :
        {
            return (OnNCHitTest(hwnd, LOWORD(lParam), HIWORD(lParam),FALSE));
        }
        break;

        case WM_NET_DB_UPDATE_BATCH :
        {
            LPCDOPT pOpts = GetCDOpt();
            if (pOpts)
            {
                pOpts->DownLoadCompletion(0,NULL);
            }
        }
        break;

        case WM_NET_DB_UPDATE_DISC :
        {
            LPCDOPT pOpts = GetCDOpt();
            if (pOpts)
            {
                pOpts->DiscChanged((LPCDUNIT)lParam);
            }
        }
        break;

        //download finished on a disc
        case WM_NET_INCMETER : //download finished on discid <lparam>
        {
            //lparam == -1 means the provider failed the validation check
            if (lParam == (LPARAM)-1)
            {
                SendMessage(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),WM_LED_DOWNLOAD,0,(LPARAM)FALSE);
                HandleBadServiceProvider(hwnd);
                break;
            }

            if (lParam != 0) 
            {
                MMNET mmNet;
                mmNet.discid = (DWORD)(lParam);
                mmNet.hwndCallback = hwnd;
                mmNet.pData = (void*)GetCDData();
                mmNet.pData2 = NULL;
                mmNet.fForceNet = FALSE;
                SendMessage(hwndCurrentComp,WM_COMMAND,MAKEWPARAM(ID_CDUPDATE,0),(LPARAM)&mmNet);

                LPCDOPT pOpt = GetCDOpt();
                if (pOpt)
                {
                    LPCDUNIT pUnit = pOpt->GetCDOpts()->pCDUnitList;
                    int nCurrDrive = 0;
                
                    while (pUnit!=NULL)
                    {
                        if (pUnit->dwTitleID == mmNet.discid)
                        {
                            pUnit->fDownLoading = FALSE;
                            break;
                        }
                        pUnit = pUnit->pNext;
                    }
                    
                    LPCDOPTIONS pCDOpts = pOpt->GetCDOpts();
                    pCDOpts->dwBatchedTitles = GetNumBatchedTitles();

                    pOpt->DownLoadCompletion(1,&(mmNet.discid));
                }

                //put up a message box if the title still isn't available
	            IMMComponentAutomation* pAuto = NULL;
	            HRESULT hr = pNodeCurrent->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&pAuto);
	            if ((SUCCEEDED(hr)) && (pAuto != NULL))
	            {
                    MMMEDIAID mmMedia;
                    mmMedia.nDrive = -1;
                    pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);
                    pAuto->Release();

                    if (mmNet.discid == mmMedia.dwMediaID)
                    {
                        LPCDDATA pData = GetCDData();
                        if (!pData->QueryTitle(mmNet.discid))
                        {
                            TCHAR szNotFound[MAX_PATH];
                            LoadString(hInst,IDS_TITLE_NOT_FOUND,szNotFound,sizeof(szNotFound)/sizeof(TCHAR));
                            MessageBox(hwnd,szNotFound,szAppName,MB_ICONINFORMATION|MB_OK);
                        }
                    } //end if disc is same as what is showing in player
                } //end if pauto ok
            }
        }
        break;

        case WM_NET_DB_FAILURE :
        {
            TCHAR szDBError[MAX_PATH];
            LoadString(hInst,IDS_DB_FAILURE,szDBError,sizeof(szDBError)/sizeof(TCHAR));
            MessageBox(hwnd,szDBError,szAppName,MB_ICONERROR|MB_OK);
        }
        break;

        case WM_NET_NET_FAILURE :
        {
            TCHAR szNetError[MAX_PATH];
            LoadString(hInst,IDS_NET_FAILURE,szNetError,sizeof(szNetError)/sizeof(TCHAR));
            MessageBox(hwnd,szNetError,szAppName,MB_ICONERROR|MB_OK);
        }
        break;

        case WM_NET_DONE :
        {
            if (lParam == 0)
            {
                //if lparam is 0, download is done ... nuke the animation
                SendMessage(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),WM_LED_DOWNLOAD,0,(LPARAM)FALSE);
            }
        }
        break;

        //Network status callback
        case WM_NET_STATUS : //download information string in <lparam>
        {
            //we basically just ignore the string messages and start
            //the animation if one isn't going already
            SendMessage(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),WM_LED_DOWNLOAD,0,(LPARAM)TRUE);
        }
        break;

        case WM_NET_CHANGEPROVIDER :
        {
            SendMessage(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),WM_NET_CHANGEPROVIDER,wParam,lParam);
        }
        break;

        //change the title bar contents
        case WM_SIZE :
        {
            if (wParam != SIZE_MINIMIZED)
            {
                TCHAR szText[MAX_PATH];
                _tcscpy(szText,szAppName);
                if (pNodeCurrent)
                {            
                    if (_tcslen(pNodeCurrent->szTitle) > 0)
                    {
                        wsprintf(szText,TEXT("%s - %s"),pNodeCurrent->szTitle,szAppName);
                    }
                }
            
                SetWindowText(hwnd,szText);
            }
        }
        break;

        //notification that the current disc drive is different
        case WM_DISCCHANGED :
        {
            LPCDOPT pOpt = GetCDOpt();
            if( pOpt )
            {
                LPCDOPTIONS pCDOpts = pOpt->GetCDOpts();
                if (InitCDVol(hwnd,pCDOpts))
                {
                    DWORD dwVol = GetVolume();
                    CKnob* pKnob = GetKnobFromID(hwndMain,IDB_VOLUME);
                    if (pKnob!=NULL)
                    {
                        pKnob->SetPosition(dwVol,(BOOL)lParam);
                    }

                    SendMessage(GetDlgItem(hwnd,IDB_MUTE),BM_SETSTATE,(WPARAM)GetMute(),0);
                    SendMessage(GetDlgItem(hwndCurrentComp,IDC_LEDWINDOW),WM_LED_MUTE,0,GetMute());
                }
            } //end if popt

            //if no disc in player, gray out the track button
	        IMMComponentAutomation* pAuto = NULL;
	        HRESULT hr = pNodeCurrent->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&pAuto);
	        if ((SUCCEEDED(hr)) && (pAuto != NULL))
	        {
                MMMEDIAID mmMedia;
                mmMedia.nDrive = -1;
                pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);
                pAuto->Release();
                if (mmMedia.dwMediaID == 0)
                {
                    EnableWindow(GetDlgItem(hwnd,IDB_TRACK),FALSE);
                }
            }
        }
        break;
    
	    case WM_SYSCOLORCHANGE :
        case WM_DISPLAYCHANGE :
        {
            //user may have turned on high-contrast mode,
            //or changed the display depth.  Either way,
            //it may be time to change bitmaps
            HandleDisplayChange();
        }
        break;
        
        case WM_ERASEBKGND :
        {
            EnumDisplayMonitors((HDC)wParam, NULL, DoPaint, (LPARAM)hwnd);
            return TRUE;
        }
        break;

        case WM_SETCURSOR :
        {
            if ((HWND)wParam == GetDlgItem(hwnd,IDB_MUTE))
            {
                if (hCursorMute)
                {
                    SetCursor(hCursorMute);
                    return TRUE;
                }
            }
        }
        break;

        case WM_HELP :
        {
            char chDst[MAX_PATH];
            WideCharToMultiByte(CP_ACP, 0, HELPFILENAME, 
								            -1, chDst, MAX_PATH, NULL, NULL); 
            HtmlHelpA(GetDesktopWindow(), chDst, HH_DISPLAY_TOPIC, 0);
        }
        break;

        case WM_CLOSE :
        {
            if ((fShellMode) && IsWindowVisible(hwnd))
            {
                ShowWindow(hwnd,SW_HIDE);
                return 0;
            }
        }
        break;

        case WM_PAINT :
	    {
            //multi-mon paint
	        HDC hdc;
	        PAINTSTRUCT ps;
	        hdc = BeginPaint( hwnd, &ps );
            EnumDisplayMonitors(hdc, NULL, DoPaint, (LPARAM)hwnd);
            EndPaint(hwnd, &ps);
            return 0;
	    }
        break;

        //default push putton handler
        case DM_SETDEFID :
        {
            wDefButtonID = (WORD)wParam;
        }
        break;

        case DM_GETDEFID :
        {
            return (MAKELRESULT(wDefButtonID,DC_HASDEFID));
        }
        break;

        //custom menu accel handler
        case WM_MENUCHAR :
        {
            if (g_pMenu)
            {
                return (g_pMenu->MenuChar((TCHAR)LOWORD(wParam),(UINT)HIWORD(wParam),(HMENU)lParam));
            }
        }
        break;
        
        //custom menu handler
        case WM_MEASUREITEM :
        {
	        if (lParam == 0) return (0);

            if (g_pMenu)
            {
                g_pMenu->MeasureItem(hwnd,(LPMEASUREITEMSTRUCT)lParam);
            }
        }
        break;

        //custom menu/button handler
	    case WM_DRAWITEM :
	    {
	        if (lParam == 0) return (0);

            if (wParam == 0)
            {
                if (g_pMenu)
                {
                    g_pMenu->DrawItem(hwnd,(LPDRAWITEMSTRUCT)lParam);
                    return (1);
                }
            }
            else
            {
    	        DrawButton((UINT)wParam,(LPDRAWITEMSTRUCT)lParam);
                return (1);
            }

	        return (0);
	    }

        //notify is either from tool tip or volume knob	    
        case WM_NOTIFY :
	    {
            if ((((LPNMHDR)lParam)->code) == TTN_NEEDTEXT)
            {
                OnToolTipNotify(lParam);
            }
            else
            {
	            if ((int)wParam == IDB_VOLUME)
	            {
		            CKnob* pKnob = GetKnobFromID(hwnd,IDB_VOLUME);

		            DWORD dwNewVol = pKnob->GetPosition();

		            DrawVolume(pKnob->GetPosition());

		            if ((((LPNMHDR)lParam)->code) == TRUE)
                    {
                        SetVolume(dwNewVol);
                    }

		            //reset timer to repaint client area
		            KillTimer(hwnd,VOLUME_PERSIST_TIMER_EVENT);
		            SetTimer(hwnd,VOLUME_PERSIST_TIMER_EVENT,VOLUME_PERSIST_TIMER_RATE,(TIMERPROC)VolPersistTimerProc);
	            } //end if knob
            } //end else
	    }
	    break;
	    
	    //menu going away
        case WM_EXITMENULOOP :
        {
            BlockMenu(hwnd);
        }
        break;
        
        //command message
        case WM_COMMAND :
	    {
            HandleCommand(hwnd, iMsg, wParam, lParam);
	    }
	    break;

	    case WM_DEVICECHANGE :
	    {
            Volume_DeviceChange(hwnd, wParam, lParam);
	    }
        break;

        case WM_WININICHANGE :
        {
	        return (SendMessage(hwndCurrentComp,WM_WININICHANGE,wParam,lParam));
        }

	    //we're done
        case WM_ENDSESSION :
        case WM_DESTROY :
	    {
            if ((iMsg == WM_ENDSESSION) && (!wParam))
            {
                return 0;
            }

            if (hMutex)
            {
                ReleaseMutex(hMutex);
                CloseHandle(hMutex);
                hMutex = NULL;
            }

            //if playing and we don't want to be, stop it
            //BE VERY PARANOID and check all variables, this is an RTMCRIT bug fix
            LPCDOPT pOpt = GetCDOpt();
            if (pOpt)
            {
                LPCDOPTIONS pOptions = pOpt->GetCDOpts();
                if (pOptions)
                {
                    LPCDOPTDATA pOptionData = pOptions->pCDData;
                    if (pOptionData)
                    {
	                    if (pOptionData->fExitStop)
                        {
	                        IMMComponentAutomation* pAuto = NULL;
                            if (pNodeCurrent)
                            {
	                            HRESULT hr = pNodeCurrent->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&pAuto);
	                            if ((SUCCEEDED(hr)) && (pAuto != NULL))
	                            {
                                    //at this point, we can actually stop the CD
                                    pAuto->OnAction(MMACTION_STOP,NULL);        
                                    //release the automation object        
                                    pAuto->Release();
                                }
                            } //end if pnodecurrent
                        } //end if "stop on exit"
                    } //end if option data ok
                } //end if options OK
            } //end if opt OK

            //make sure we're not downloading
            EndDownloadThreads();

			//Unregister the WM_DEVICECHANGE notification
			Volume_DeviceChange_Cleanup();
            
	        //close the volume mixer
            mixerClose((HMIXER)hmix);

            //delete any GDI objects
	        GlobalFree(hbmpMain);
	        GlobalFree(hbmpMainRestore);
            GlobalFree(hbmpMainSmall);
            GlobalFree(hbmpMainNoBar);
	        DeleteObject(hpalMain);

	        //shut down the button class
	        UninitMButtons();

	        //save window state
            if (!IsIconic(hwnd))
            {
                RECT rect;
	            GetWindowRect(hwnd,&rect);
	            SetSettings(rect.left,rect.top);
            }
	        
	        PostQuitMessage(0);
	        return (0);
	    }
    }

    if (iMsg == g_uTaskbarRestart)
    {
        if (fShellMode)
        {
            CreateShellIcon(hInst,hwndMain,pNodeCurrent,szAppName);
        }
    }

    if (iMsg == giVolDevChange)
    {
        WinMMDeviceChangeHandler(hwnd);
    }

    return (DefWindowProc(hwnd, iMsg, wParam, lParam));
}

////////////////////////////////////////////////////////////////////////////////////////////
// * LoadComponents
// Load registered componets from registry
// This code modified from the original MFC-based "Jazz" implementation, with blessings
// from todorfay <g>
////////////////////////////////////////////////////////////////////////////////////////////
BOOL LoadComponents( void )
{
	BOOL      fSuccess = FALSE;
	IMMComponent* pIComponent = NULL;
	TCHAR     szError[MAX_PATH];

	if( SUCCEEDED(CDPLAY_CreateInstance(NULL, IID_IMMComponent, (void**)&pIComponent)) )
	{
		fSuccess = TRUE;
        AddComponent(pIComponent);
	}
	else
	{
		TCHAR strMsg[MAX_PATH];
    	LoadString(hInst,IDS_ERRORLOADINGCOMP,szError,sizeof(szError)/sizeof(TCHAR));
        TCHAR strReinstall[MAX_PATH];
    	LoadString(hInst,IDS_REINSTALL,strReinstall,sizeof(strReinstall)/sizeof(TCHAR));
		wsprintf(strMsg,TEXT("%s\n\n%s"), szError, strReinstall);
		MessageBox(NULL, strMsg, szAppName, MB_OK|MB_ICONERROR );
	}

	return fSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * AddComponent
// Add a component to the list of comps
////////////////////////////////////////////////////////////////////////////////////////////
void AddComponent(IMMComponent* pComponent)
{
	pCompListTail->pComp = pComponent;
	pCompListTail->pSink = new CFrameworkNotifySink(pCompListTail);
    pCompListTail->pSink->AddRef();

	PCOMPNODE pNew = new COMPNODE;
	pNew->pComp = NULL;
	pNew->pNext = NULL;
	pNew->hwndComp = NULL;
	pNew->pSink = NULL;
	pNew->szTitle[0] = '\0';

	pCompListTail->pNext = pNew;
	pCompListTail = pNew;
}

////////////////////////////////////////////////////////////////////////////////////////////
// * CleanUp
// Get rid of anything that might have been around, like linked list, mutex, bitmaps, etc.
////////////////////////////////////////////////////////////////////////////////////////////
void CleanUp(void)
{
    if (pCompList)
    {
        while (pCompList->pNext != NULL)
        {
	        PCOMPNODE pTemp = pCompList;
	        pCompList = pTemp->pNext;
	        if (pTemp->pComp)
	        {
	            pTemp->pComp->Release();
	            pTemp->pSink->Release();
	        }
	        delete pTemp;
        }
    
        delete pCompList;
        pCompList = NULL;
    }

    if (g_pOptions)
    {
        g_pOptions->Release();
    }

    if (g_pData)
    {
        g_pData->Release();
    }

    if (g_hhk)
    {
        UnhookWindowsHookEx(g_hhk);
        g_hhk = NULL;
    }

    if (hmImage)
    {
        FreeLibrary(hmImage);
    }

    if (fShellMode)
    {
        DestroyShellIcon();
    }

    CDNET_Uninit();
}

////////////////////////////////////////////////////////////////////////////////////////////
// * InitComponents
// Initialize components by calling their INIT functions and setting their window sizes
////////////////////////////////////////////////////////////////////////////////////////////
void InitComponents(HWND hwnd)
{
    PCOMPNODE pList = pCompList;

    RECT rect;
    while (pList->pNext!=NULL)
    {
	    IMMComponent* pComp = pList->pComp;
	    if (pComp)
	    {
	        nNumComps++;
	        
	        pComp->Init(pList->pSink,hwnd,&rect,&(pList->hwndComp),&(pList->hmenuComp));

            CalculateDispAreaOffset(pComp);

            switch (g_nViewMode)
            {
                case VIEW_MODE_NORMAL :
                {
                    SetRect(&rect,24,32,455,88+nDispAreaOffset);
                }
                break;

                case VIEW_MODE_RESTORE :
                {
                    SetRect(&rect,303,24,384,41);
                }
                break;

                case VIEW_MODE_SMALL :
                {
                    SetRect(&rect,303,12,384,29);
                }
                break;

                case VIEW_MODE_NOBAR :
                {
                    SetRect(&rect,24,16,455,72+nDispAreaOffset);
                }
                break;
            }

	        SetWindowPos(pList->hwndComp,
		         hwnd,
		         rect.left,
		         rect.top,
		         rect.right - rect.left,
		         rect.bottom - rect.top,
		         SWP_NOZORDER|SWP_NOACTIVATE);

            //size ledwindow to maximum size
            HWND hwndLED = GetDlgItem(pList->hwndComp,IDC_LEDWINDOW);
            if (hwndLED)
            {
                SetRect(&rect,24,32,455,88+nDispAreaOffset);
	            SetWindowPos(hwndLED,
		             pList->hwndComp,
		             0,
		             0,
		             rect.right - rect.left,
		             rect.bottom - rect.top,
		             SWP_NOZORDER|SWP_NOACTIVATE);

                InvalidateRect(hwndLED,NULL,FALSE);
                UpdateWindow(hwndLED);
            }

	        if (!hwndCurrentComp)
	        {
		        ShowNewComponentWindow(pList, hwnd);
	        }
	    } //end if comp ok

	    pList = pList->pNext;
    } //end while
}

////////////////////////////////////////////////////////////////////////////////////////////
// * GetToolTipMsgProc
// Msg hook for tool tips so they know when to pop up
////////////////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK GetToolTipMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{ 
    MSG *lpmsg;
    lpmsg = (MSG *) lParam;
     
    if (nCode < 0 || !(IsChild(hwndMain, lpmsg->hwnd)))
    {
        return (CallNextHookEx(g_hhk, nCode, wParam, lParam));  
    }

    switch (lpmsg->message)
    {
        case WM_MOUSEMOVE: 
        case WM_LBUTTONDOWN:         
        case WM_LBUTTONUP: 
        case WM_RBUTTONDOWN:         
        case WM_RBUTTONUP: 
            if (g_hwndTT != NULL)
            {                 
                MSG msg;  
                msg.lParam = lpmsg->lParam; 
                msg.wParam = lpmsg->wParam; 
                msg.message = lpmsg->message;                 
                msg.hwnd = lpmsg->hwnd; 
                
                SendMessage(g_hwndTT, TTM_RELAYEVENT, 0, 
                    (LPARAM) (LPMSG) &msg);             
            }             
            break; 
        
        default: break;     
    } 

    return (CallNextHookEx(g_hhk, nCode, wParam, lParam));
} 
    
////////////////////////////////////////////////////////////////////////////////////////////
// * CreateToolTips
// Common control setup code to init tool tips
////////////////////////////////////////////////////////////////////////////////////////////
BOOL CreateToolTips(HWND hwnd)
{  
    InitCommonControls(); 

    g_hwndTT = CreateWindowEx(0, TOOLTIPS_CLASS, (LPTSTR) NULL, 
        TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
        CW_USEDEFAULT, hwnd, (HMENU) NULL, hInst, NULL);
          
    if (g_hwndTT == NULL)
        return FALSE;  

    // Install a hook procedure to monitor the message stream for mouse 
    // messages intended for the controls in main window
    g_hhk = SetWindowsHookEx(WH_GETMESSAGE, GetToolTipMsgProc, 
        (HINSTANCE) NULL, GetCurrentThreadId());  

    if (g_hhk == NULL)
        return FALSE;
        
    return TRUE; 	
} 


//WM_DEVICECHANGE support/////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////

void Volume_DeviceChange_Cleanup()
{
   if (DeviceEventContext) 
   {
       UnregisterDeviceNotification(DeviceEventContext);
       DeviceEventContext = 0;
   }
}

/*
**************************************************************************************************
	Volume_GetDeviceHandle()

	given a mixerID this functions opens its corresponding device handle. This handle can be used 
	to register for DeviceNotifications.

	dwMixerID -- The mixer ID
	phDevice -- a pointer to a handle. This pointer will hold the handle value if the function is
				successful
	
	return values -- If the handle could be obtained successfully the return vlaue is TRUE.

**************************************************************************************************
*/
BOOL Volume_GetDeviceHandle(DWORD dwMixerID, HANDLE *phDevice)
{
	MMRESULT mmr;
	ULONG cbSize=0;
	TCHAR *szInterfaceName=NULL;

	//Query for the Device interface name
	mmr = mixerMessage((HMIXER)ULongToPtr(dwMixerID), DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&cbSize, 0L);
	if(MMSYSERR_NOERROR == mmr)
	{
		szInterfaceName = (TCHAR *)GlobalAllocPtr(GHND, (cbSize+1)*sizeof(TCHAR));
		if(!szInterfaceName)
		{
			return FALSE;
		}

		mmr = mixerMessage((HMIXER)ULongToPtr(dwMixerID), DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)szInterfaceName, cbSize);
		if(MMSYSERR_NOERROR != mmr)
		{
			GlobalFreePtr(szInterfaceName);
			return FALSE;
		}
	}
	else
	{
		return FALSE;
	}

	//Get an handle on the device interface name.
	*phDevice = CreateFile(szInterfaceName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
						 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	GlobalFreePtr(szInterfaceName);
	if(INVALID_HANDLE_VALUE == *phDevice)
	{
		return FALSE;
	}

	return TRUE;
}


/*	Volume_DeviceChange_Init()
*	First time initialization for WM_DEVICECHANGE messages
*	
*	On NT 5.0, you have to register for device notification
*/
void Volume_DeviceChange_Init(HWND hWnd, DWORD dwMixerID)
{

	DEV_BROADCAST_HANDLE DevBrodHandle;
	HANDLE hMixerDevice=NULL;

	//If we had registered already for device notifications, unregister ourselves.
	Volume_DeviceChange_Cleanup();

	//If we get the device handle register for device notifications on it.
	if(Volume_GetDeviceHandle(dwMixerID, &hMixerDevice))
	{
		memset(&DevBrodHandle, 0, sizeof(DEV_BROADCAST_HANDLE));

		DevBrodHandle.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
		DevBrodHandle.dbch_devicetype = DBT_DEVTYP_HANDLE;
		DevBrodHandle.dbch_handle = hMixerDevice;

		DeviceEventContext = RegisterDeviceNotification(hWnd, &DevBrodHandle, 
													DEVICE_NOTIFY_WINDOW_HANDLE);

		if(hMixerDevice)
		{
			CloseHandle(hMixerDevice);
			hMixerDevice = NULL;
		}
	}
}


void Volume_DeviceChange(HWND hDlg, WPARAM wParam, LPARAM lParam)
{
    //if plug-and-play sends this, pass it along to the component
    PDEV_BROADCAST_HANDLE bh = (PDEV_BROADCAST_HANDLE)lParam;

    //If we have an handle on the device then we get a DEV_BROADCAST_HDR structure as the lParam.
    //Or else it means that we have registered for the general audio category KSCATEGORY_AUDIO.
	    
    if(!DeviceEventContext || !bh || bh->dbch_devicetype != DBT_DEVTYP_HANDLE)
    {
        SendMessage(hwndCurrentComp,WM_DEVICECHANGE,wParam,lParam);
    }
    else
    {
        //Handle device changes to the mixer device.
        switch(wParam)
        {
	        //send "1" in lparam to indicate that this is a device remove and not a power request

	        case DBT_DEVICEQUERYREMOVE:
                HandlePowerBroadcast(hwndMain, PBT_APMQUERYSUSPEND, (LPARAM)1);
		        break;
	        
	        case DBT_DEVICEQUERYREMOVEFAILED:
		        HandlePowerBroadcast(hwndMain, PBT_APMQUERYSUSPENDFAILED, (LPARAM)1);
		        break;
        }
    }
}

//WM_DEVICECHANGE support ends////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////