windows-nt/Source/XPSP1/NT/shell/osshell/control/mmsys/class.c

2319 lines
61 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*==========================================================================*/
//
// class.c
//
// Copyright (C) 1993-1994 Microsoft Corporation. All Rights Reserved.
// Mod Log: Modified by Shawn Brown (10/95)
// - Ported to NT (Unicode, etc.)
/*==========================================================================*/
#include "mmcpl.h"
#include <windowsx.h>
#include <mmsystem.h>
#include <mmddk.h>
#include <cpl.h>
#define NOSTATUSBAR
#include <commctrl.h>
#include <prsht.h>
#include <string.h>
#include <memory.h>
#include <idf.h>
#include <regstr.h>
#include "utils.h"
#include "midi.h"
#include "mmdebug.h"
#if defined DEBUG || defined DEBUG_RETAIL
extern TCHAR szNestLevel[];
#endif
#include "medhelp.h"
#ifndef TVIS_ALL
#define TVIS_ALL 0xFF7F
#endif
static CONST TCHAR cszIdfWildcard[] = TEXT ("*.idf");
static CONST TCHAR cszIdf[] = TEXT (".idf");
static CONST TCHAR cszSetupKey[] = REGSTR_PATH_SETUP REGSTR_KEY_SETUP;
static CONST TCHAR cszMachineDir[] = REGSTR_VAL_WINDIR;
static CONST TCHAR cszConfigDir[] = TEXT ("config\\");
extern CONST TCHAR cszMidiSlash[];
extern CONST TCHAR cszFriendlyName[];
extern CONST TCHAR cszDescription[];
extern CONST TCHAR cszSlashInstruments[];
extern CONST TCHAR cszExternal[];
extern CONST TCHAR cszDefinition[];
extern CONST TCHAR cszPort[];
extern CONST TCHAR cszDriversRoot[];
extern CONST TCHAR cszSchemeRoot[];
extern CONST TCHAR cszMidiMapRoot[];
extern CONST TCHAR cszDriversRoot[];
extern CONST TCHAR csz02d[];
extern CONST TCHAR cszSlash[];
extern CONST TCHAR cszEmpty[];
extern int lstrnicmp (LPTSTR pszA, LPTSTR pszB, size_t cch);
typedef struct _midi_class {
LPPROPSHEETPAGE ppsp;
HKEY hkMidi;
BOOL bDetails;
BOOL bRemote; // device connected via midi cable
UINT bChanges;
UINT ixDevice; // registry enum index of driver key
BYTE nPort;
BYTE bFill[3];
BOOL bFillingList;
#ifdef USE_IDF_ICONS
HIMAGELIST hIDFImageList;
#endif
LPTSTR pszKey;
TCHAR szFullKey[MAX_PATH];
TCHAR szAlias[MAX_PATH];
TCHAR szFile[MAX_PATH*2];
} MCLASS, FAR * PMCLASS;
#define MCL_ALIAS_CHANGED 1
#define MCL_TREE_CHANGED 2
#define MCL_IDF_CHANGED 4
#define MCL_PORT_CHANGED 8
/*+
* Determines if a given string has a given prefix and if
* the next character in the string is a given charater.
*
* if so, it returns a pointer to the first character in the
* string after the prefix.
*
* this is useful for parsing off the file in file<Instrument>
* or parts of registry paths.
*
* note that we do NOT consider a string to be a prefix of itself.
* psz MUST be longer than than pszPrefix or this function returns NULL.
*
*-=================================================================*/
STATICFN LPTSTR WINAPI IsPrefix (
LPTSTR pszPrefix,
LPTSTR psz,
TCHAR chTerm)
{
UINT cb = lstrlen(pszPrefix);
UINT cb2 = lstrlen(psz);
TCHAR ch;
if (cb2 < cb)
return NULL;
ch = psz[cb];
if (ch != chTerm)
return NULL;
psz[cb] = 0;
if (lstrcmpi(pszPrefix, psz))
{
psz[cb] = ch;
return NULL;
}
psz[cb] = ch;
return psz + cb;
}
/*+ IsFullPath
*
* returns true if the filename passed in is a fully qualified
* pathname. returns false if it is a relative path
*
* unc paths are treated as fully qualified always
*
*-=================================================================*/
BOOL IsFullPath (
LPTSTR pszFile)
{
// fully qualified paths either begin with a backslash
// or with a drive letter, colon, then backslash
//
if ((pszFile[0] == TEXT('\\')) ||
(pszFile[1] == TEXT(':') && pszFile[2] == TEXT('\\')))
return TRUE;
return FALSE;
}
/*+ GetIDFDirectory
*
*-=================================================================*/
BOOL GetIDFDirectory (
LPTSTR pszDir,
UINT cchDir)
{
HKEY hKey;
UINT cbSize;
*pszDir = 0;
#if(_WIN32_WINNT >= 0x0400)
if (!GetSystemDirectory (pszDir, cchDir))
return FALSE;
#else
if (!RegOpenKey (HKEY_LOCAL_MACHINE, cszSetupKey, &hKey))
{
cbSize = cchDir * sizeof(TCHAR);
RegQueryValueEx (hKey,
cszMachineDir,
NULL,
NULL,
(LPBYTE)pszDir,
&cbSize);
RegCloseKey (hKey);
cchDir = cbSize/sizeof(TCHAR);
if (!cchDir--)
return FALSE;
}
else if (!GetWindowsDirectory (pszDir, cchDir))
return FALSE;
#endif
cchDir = lstrlen (pszDir);
if (pszDir[cchDir -1] != TEXT('\\'))
pszDir[cchDir++] = TEXT('\\');
lstrcpy (pszDir + cchDir, cszConfigDir);
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT("IDFDir='%s'\r\n"), pszDir);
#endif
return TRUE;
}
/*+ GetIDFFileName
*
*-=================================================================*/
BOOL GetIDFFileName (
HWND hWnd,
LPTSTR lpszFile,
UINT cchFile)
{
OPENFILENAME ofn;
TCHAR szFilter[MAX_PATH];
UINT cch;
assert (hWnd);
// load filter string from resource and convert '#' characters
// into NULLs
//
LoadString (ghInstance, IDS_IDFFILES, szFilter, NUMELMS(szFilter));
cch = lstrlen(szFilter);
assert2 (cch, TEXT ("IDFFILES resource is empty!"));
while (cch--)
{
if (TEXT('#') == szFilter[cch])
szFilter[cch] = 0;
}
ZeroMemory (&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.hInstance = ghInstance;
ofn.lpstrFilter = szFilter;
ofn.nFilterIndex = 1;
ofn.lpstrFile = lpszFile;
ofn.nMaxFile = cchFile;
ofn.Flags = OFN_HIDEREADONLY | OFN_NOCHANGEDIR;
ofn.lpstrDefExt = cszIdf;
return GetOpenFileName (&ofn);
}
/*+ InstallNewIDF
*
*-=================================================================*/
BOOL WINAPI InstallNewIDF (
HWND hWnd)
{
TCHAR szWinPath[MAX_PATH];
TCHAR szNewIDF[MAX_PATH];
UINT cch;
UINT oBasename;
// prompt for an IDF file
//
szNewIDF[0] = 0;
if ( ! GetIDFFileName (hWnd, szNewIDF, NUMELMS(szNewIDF)))
return FALSE;
// set oBasename to pointer to the first character of the
// basename of the new idf file
//
oBasename = lstrlen (szNewIDF);
if (!oBasename)
return FALSE;
while (oBasename && (TEXT('\\') != szNewIDF[oBasename-1]))
--oBasename;
// build the new filename from windows directory and idf basename
//
GetIDFDirectory (szWinPath, NUMELMS(szWinPath));
cch = lstrlen (szWinPath);
if (cch && szWinPath[cch-1] != TEXT('\\'))
szWinPath[cch++] = TEXT('\\');
lstrcpyn (szWinPath + cch, szNewIDF + oBasename, NUMELMS(szWinPath)-cch);
oBasename = cch;
#ifdef DEBUG
AuxDebugEx (5, DEBUGLINE TEXT("install IDF to '%s'\r\n"), szWinPath);
#endif
// now force .idf as the extension for new file
//
for (cch = lstrlen (szWinPath); cch && szWinPath[cch] != TEXT('.'); --cch)
if (TEXT('\\') == szWinPath[cch])
{
cch = lstrlen(szWinPath);
break;
}
lstrcpy (szWinPath + cch, cszIdf);
// quit now if we are trying to copy a file to itself
//
if (IsSzEqual(szWinPath, szNewIDF))
return FALSE;
// copy the file, but fail if destination already exists
//
#ifdef DEBUG
AuxDebugEx (5, DEBUGLINE TEXT("Copying %s to %s\r\n"), szNewIDF, szWinPath);
#endif
if (CopyFile (szNewIDF, szWinPath, TRUE))
return TRUE;
//
// if copy fails, query to overwrite because destination
// already exists.
//
else
{
TCHAR szQuery[255];
TCHAR sz[255];
#ifdef DEBUG
AuxDebugEx (1, DEBUGLINE TEXT ("InstallIDF -CopyFile failed w/ %d\r\n"),
GetLastError());
#endif
LoadString (ghInstance, IDS_QUERY_OVERIDF, sz, NUMELMS(sz));
wsprintf (szQuery, sz, szWinPath + oBasename);
LoadString (ghInstance, IDS_IDF_CAPTION, sz, NUMELMS(sz));
if (MessageBox (hWnd, szQuery, sz, MB_YESNO | MB_ICONQUESTION) == IDYES)
return CopyFile (szNewIDF, szWinPath, FALSE);
}
return FALSE;
}
/*+
*
* FEATURE: Please remove the #ifdef UNICODE sections
* when mmioOpen gets UNICODE enabled !!!
*-=================================================================*/
typedef BOOL (WINAPI * FNIDFENUM)(LPVOID pvArg,
UINT nEnum,
LPIDFHEADER pHdr,
LPIDFINSTINFO pInst);
UINT WINAPI idfEnumInstruments (
LPTSTR lpszFile,
FNIDFENUM fnEnum,
LPVOID lpvArg)
{
MMCKINFO chkIDFX; // Grandparent chunk
MMCKINFO chkMMAP; // Parent chunk
HMMIO hmmio; // Handle to the file.
UINT nInstruments;
#ifdef DEBUG
AuxDebugEx (3, DEBUGLINE TEXT("idfEnumInstruments('%s',%08X,%08X)\r\n"),
lpszFile, fnEnum, lpvArg);
#endif
// Open the file for reading.
hmmio = mmioOpen(lpszFile, NULL, MMIO_READ);
if ( ! hmmio)
{
// What were they thinking?? You can't assert this.
// assert3(0, TEXT("Cant open IDF file %s"), lpszFile ? lpszFile : TEXT("<null>"));
return 0;
}
// the whole IDF instrument stuff is wrapped in an 'IDF ' RIFF chunk
//
chkIDFX.fccType = MAKEFOURCC('I','D','F',' ');
if (mmioDescend(hmmio, &chkIDFX, NULL, MMIO_FINDRIFF))
{
#ifdef DEBUG
AuxDebugEx (0, DEBUGLINE TEXT ("idfEnum: '%s' is not a valid IDF File\r\n"), lpszFile);
#endif
mmioClose(hmmio, 0);
return 0;
}
// Count the number of instruments by counting
// the number of "MMAP"'s in the file.
//
nInstruments = 0;
chkMMAP.fccType = MAKEFOURCC('M','M','A','P');
while ( ! mmioDescend(hmmio, &chkMMAP, &chkIDFX, MMIO_FINDLIST))
{
union {
IDFHEADER idf;
TCHAR sz[MAX_ALIAS + sizeof(IDFHEADER)];
} hdr;
union {
IDFINSTINFO iii;
BYTE ab[MAX_ALIAS * 8 + sizeof(IDFINSTINFO)];
} inst;
MMCKINFO chk;
DWORD cb;
#ifdef DEBUG
AuxDebugEx (15, DEBUGLINE TEXT ("MMAP[%d] id=%08X siz=%08x\r\n"),
nInstruments, chkMMAP.ckid, chkMMAP.cksize);
#endif
// read the hdr chunk
//
chk.ckid = MAKEFOURCC('h','d','r',' ');
if (mmioDescend(hmmio, &chk, &chkMMAP, MMIO_FINDCHUNK))
break;
#ifdef DEBUG
AuxDebugEx (15, DEBUGLINE TEXT(" hdr.id=%08X hdr.siz=%08x\r\n"),
chk.ckid, chk.cksize);
#endif
assert (chk.cksize > 0 && chk.cksize < 0x0080000);
//AuxDebugDump (6, &chk, sizeof(chk));
cb = min(chk.cksize, sizeof(hdr));
if ((DWORD)mmioRead (hmmio, (LPVOID)&hdr, cb) != cb)
break;
//AuxDebugDump (6, &chk, sizeof(chk));
hdr.sz[NUMELMS(hdr.sz)-1] = 0;
mmioAscend (hmmio, &chk, 0);
#ifdef DEBUG
AuxDebugEx (15, DEBUGLINE TEXT("hdr = '%s'\r\n"), hdr.idf.abInstID);
#endif
//AuxDebugDump (6, &chk, sizeof(chk));
// read the inst chunk and locate the product name
// field.
//
chk.ckid = MAKEFOURCC('i','n','s','t');
if (mmioDescend(hmmio, &chk, &chkMMAP, MMIO_FINDCHUNK))
{
#ifdef DEBUG
AuxDebug (TEXT ("mmioDescend failed for 'inst' chunk"));
#endif
}
#ifdef DEBUG
AuxDebugEx (15, DEBUGLINE TEXT(" inst.id=%08X inst.siz=%08x\r\n"),
chk.ckid, chk.cksize);
#endif
assert (chk.cksize > 0 && chk.cksize < 0x0080000);
cb = min(chk.cksize, sizeof(inst));
if ((DWORD)mmioRead (hmmio, (LPVOID)&inst, cb) != cb)
{
#ifdef DEBUG
AuxDebug ( TEXT ("mmioRead failed for 'inst' chunk"));
#endif
}
inst.ab[NUMELMS(inst.ab)-1] = 0;
mmioAscend (hmmio, &chk, 0);
#ifdef DEBUG
AuxDebugEx (15, DEBUGLINE TEXT ("inst.mfg = '%s'\r\n"), inst.iii.abData);
AuxDebugEx (15, TEXT ("\t.prod = '%s'\r\n"), inst.iii.abData
+ inst.iii.cbManufactASCII
+ inst.iii.cbManufactUNICODE);
#endif
// call the enum callback for this instrument
//
if ( ! fnEnum (lpvArg, nInstruments, &hdr.idf, &inst.iii))
break;
++nInstruments;
assert (nInstruments < 20);
// ascend and loop back to look for the next instrument
//
if (mmioAscend(hmmio, &chkMMAP, 0))
break;
}
mmioClose(hmmio, 0);
return nInstruments;
}
/*+ LoadTypesIntoTree
*
*-=================================================================*/
struct types_enum_data {
HANDLE hWndT;
TV_INSERTSTRUCT * pti;
LPTSTR pszInstr;
HTREEITEM htiSel;
};
STATICFN BOOL WINAPI fnTypesEnum (
LPVOID lpv,
UINT nEnum,
LPIDFHEADER pHdr,
LPIDFINSTINFO pInst)
{
struct types_enum_data * pted = lpv;
HTREEITEM hti;
assert (pted);
#ifdef DEBUG
AuxDebugEx (7, DEBUGLINE TEXT ("enum[%d] '%s' instr=%x\r\n"),
nEnum, pHdr->abInstID, pted->pszInstr);
#endif
MultiByteToWideChar(GetACP(), 0,
pHdr->abInstID, -1,
pted->pti->item.pszText, sizeof(pted->pti->item.pszText));
hti = TreeView_InsertItem (pted->hWndT, pted->pti);
// this item is the 'selected' one, if it is the first
// item or if it matches the name
//
if ((nEnum == 0) ||
(pted->pszInstr && pted->pszInstr[0] &&
IsPrefix(pted->pti->item.pszText, pted->pszInstr + sizeof(TCHAR), TEXT('>'))))
{
pted->htiSel = hti;
#ifdef DEBUG
AuxDebugEx (7, DEBUGLINE TEXT("\t'%s' hti %08X is select\r\n"),
pted->pszInstr ? pted->pszInstr : TEXT (""), hti);
#endif
}
// return true to continue enumeration
//
return TRUE;
}
STATICFN void SetTypesEdit (
HWND hWnd,
UINT uId,
PMCLASS pmcl)
{
SetDlgItemText (hWnd, uId, pmcl->szFile);
}
STATICFN void LoadTypesIntoTree (
HWND hWnd,
UINT uId,
PMCLASS pmcl)
{
HWND hWndT;
UINT cchBase;
TCHAR szPath[MAX_PATH];
TCHAR szDefaultIDF[MAX_PATH];
int ix;
WIN32_FIND_DATA ffd;
HANDLE hFind;
#ifdef USE_IDF_ICONS
HIMAGELIST hImageList;
#endif
HTREEITEM htiSelect = NULL; // item to select
hWndT = GetDlgItem (hWnd, uId);
if (!hWndT)
return;
#ifdef DEBUG
AuxDebugEx (5, DEBUGLINE TEXT ("LoadTypesIntoTree( ,%x, )\r\n"), uId);
#endif
LoadString (ghInstance, IDS_GENERAL, szDefaultIDF, NUMELMS(szDefaultIDF));
#ifdef USE_IDF_ICONS
// if we have not already loaded an image list for the IDF types
// do so now.
//
if (!(hImageList = pmcl->hIDFImageList))
{
static LPCTSTR aid[] = {
MAKEINTRESOURCE(IDI_IDFICON),
MAKEINTRESOURCE(IDI_BLANK),
};
int cx = GetSystemMetrics(SM_CXSMICON);
int cy = GetSystemMetrics(SM_CYSMICON);
DWORD dwLayout;
UINT uFlags = ILC_MASK | ILC_COLOR32;
if (GetProcessDefaultLayout(&dwLayout) &&
(dwLayout & LAYOUT_RTL))
{
uFlags |= ILC_MIRROR;
}
pmcl->hIDFImageList =
hImageList = ImageList_Create (cx, cy, uFlags, NUMELMS(aid), 2);
if (hImageList)
{
UINT ii;
for (ii = 0; ii < NUMELMS(aid); ++ii)
{
HICON hIcon = LoadImage (ghInstance, aid[ii], IMAGE_ICON,
cx, cy, LR_DEFAULTCOLOR);
if (hIcon)
ImageList_AddIcon (hImageList, hIcon);
}
}
}
#endif
pmcl->bFillingList = TRUE;
//SetWindowRedraw (hWndT, FALSE);
#ifdef DEBUG
AuxDebugEx (6, DEBUGLINE TEXT ("tv_deleteAllItems(%08X)\r\n"), hWndT);
#endif
TreeView_DeleteAllItems(hWndT);
#ifdef DEBUG
AuxDebugEx (6, DEBUGLINE TEXT ("tv_deleteAllItems(%08X) ends\r\n"), hWndT);
#endif
#ifdef USE_IDF_ICONS
TreeView_SetImageList (hWndT, hImageList, TVSIL_NORMAL);
#endif
htiSelect = NULL;
pmcl->bFillingList = FALSE;
GetIDFDirectory (szPath, NUMELMS(szPath));
cchBase = lstrlen (szPath);
if (cchBase && szPath[cchBase-1] != TEXT('\\'))
szPath[cchBase++] = TEXT('\\');
lstrcpyn (szPath + cchBase, cszIdfWildcard, NUMELMS(szPath)-cchBase);
#ifdef DEBUG
AuxDebugEx (3, DEBUGLINE TEXT ("scanning for idfs at '%s'\r\n"), szPath);
#endif
ix = 0;
hFind = FindFirstFile (szPath, &ffd);
if (hFind != INVALID_HANDLE_VALUE)
{
TV_INSERTSTRUCT ti;
struct types_enum_data ted = {hWndT, &ti, NULL, NULL};
ZeroMemory (&ti, sizeof(ti));
do
{
UINT nInstr;
UINT cch;
// patch off the extension before we add
// this name to the list
//
cch = lstrlen(ffd.cFileName);
while (cch)
if (ffd.cFileName[--cch] == TEXT('.'))
{
ffd.cFileName[cch] = 0;
break;
}
ti.hParent = TVI_ROOT;
ti.hInsertAfter = TVI_SORT;
#ifdef USE_IDF_ICONS
ti.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
#else
ti.item.mask = TVIF_TEXT | TVIF_STATE;
#endif
// the TV_ITEM structure may not be unicode enabled ?!?
ti.item.pszText = ffd.cFileName;
ti.item.state = 0;
ti.item.stateMask = TVIS_ALL;
#ifdef DEBUG
AuxDebugEx (7, DEBUGLINE TEXT ("adding '%s' to types tree\r\n"), ti.item.pszText);
#endif
ti.hParent = TreeView_InsertItem (hWndT, &ti);
if ( ! ti.hParent)
break;
ti.hInsertAfter = TVI_LAST;
// put the extension back
//
if (cch > 0)
ffd.cFileName[cch] = TEXT('.');
// check to see if this file is a match for the
// current definition file.
//
#ifdef DEBUG
AuxDebugEx (7, DEBUGLINE TEXT ("comparing '%s' with '%s'\r\n"),
ffd.cFileName, pmcl->szFile);
#endif
ted.pszInstr = IsPrefix (ffd.cFileName, pmcl->szFile, TEXT('<'));
#ifdef DEBUG
AuxDebugEx (7, DEBUGLINE TEXT ("\tpszInstr = '%s'\r\n"), ted.pszInstr ? ted.pszInstr : TEXT ("NULL"));
#endif
// add instruments as subkeys to this file
// this also has the side effect of setting ted.htiSel
// when the instrument name matches
//
lstrcpy (szPath + cchBase, ffd.cFileName);
nInstr = idfEnumInstruments (szPath, fnTypesEnum, &ted);
// if this idf has no instruments. ignore it.
// if it has more than one instrument, expand the list
// so that instruments are visible
//
if (0 == nInstr)
TreeView_DeleteItem (hWndT, ti.hParent);
else if (nInstr > 1)
TreeView_Expand (hWndT, ti.hParent, TVE_EXPAND);
else
ted.htiSel = ti.hParent;
// if we have a match on filename, then we need to select
// either the parent or one of the children
//
if (ted.pszInstr ||
IsSzEqual(ffd.cFileName,pmcl->szFile) ||
IsSzEqual(ffd.cFileName,szDefaultIDF))
{
#ifdef DEBUG
AuxDebugEx (7, DEBUGLINE TEXT ("will be selecting %08X '%s'\r\n"),
ted.htiSel, ffd.cFileName);
#endif
htiSelect = ted.htiSel;
}
} while (FindNextFile (hFind, &ffd));
FindClose (hFind);
}
if (htiSelect)
{
pmcl->bFillingList = TRUE;
#ifdef DEBUG
AuxDebugEx (7, DEBUGLINE TEXT ("selecting %08X\r\n"), htiSelect);
#endif
TreeView_SelectItem (hWndT, htiSelect);
#ifdef DEBUG
AuxDebugEx (7, DEBUGLINE TEXT ("FirstVisible %08X\r\n"), htiSelect);
#endif
TreeView_SelectSetFirstVisible (hWndT, htiSelect);
pmcl->bFillingList = FALSE;
}
#ifdef DEBUG
AuxDebugEx (5, DEBUGLINE TEXT ("LoadTypesIntoTree( ,%d, ) ends\r\n"), uId);
#endif
//SetWindowRedraw (hWndT, TRUE);
}
/*+
*
*-=================================================================*/
STATICFN void WINAPI HandleTypesSelChange (
PMCLASS pmcl,
LPNMHDR lpnm)
{
LPNM_TREEVIEW pntv = (LPVOID)lpnm;
LPTV_ITEM pti = &pntv->itemNew;
HTREEITEM htiParent;
TV_ITEM ti;
assert (pmcl->bDetails);
// setup ti to get text & # of children
// from the IDF filename entry.
//
ti.mask = TVIF_TEXT;
ti.pszText = pmcl->szFile;
ti.cchTextMax = NUMELMS(pmcl->szFile);
ti.hItem = pti->hItem;
#ifdef DEBUG
AuxDebugEx (6, DEBUGLINE TEXT ("Type Change pti=%08X hItem=%08X\r\n"), pti, pti->hItem);
#endif
// if this entry has a parent, it must be a IDF
// instrument name. if so, then we want to read
// from its parent first.
//
htiParent = TreeView_GetParent (lpnm->hwndFrom, pti->hItem);
if (htiParent)
ti.hItem = htiParent;
TreeView_GetItem (lpnm->hwndFrom, &ti);
lstrcat (pmcl->szFile, cszIdf);
#ifdef DEBUG
AuxDebugEx (6, DEBUGLINE TEXT ("mask=%08x htiParent=%08X %08x nChild=%d '%s'\r\n"),
ti.mask, htiParent, ti.hItem, ti.cChildren, ti.pszText);
#endif
// if the selection had a parent, and we are not it's first child
// then we need to append child (delimited by <>) after parent
//
if (htiParent &&
(TreeView_GetChild(lpnm->hwndFrom, htiParent) != pti->hItem))
{
static CONST TCHAR cszAngle[] = TEXT(">");
UINT cch = lstrlen(pmcl->szFile);
pmcl->szFile[cch++] = TEXT('<');
ti.mask = TVIF_TEXT;
ti.pszText = pmcl->szFile + cch;
ti.cchTextMax = NUMELMS(pmcl->szFile) - cch;
ti.hItem = pti->hItem;
TreeView_GetItem (lpnm->hwndFrom, &ti);
lstrcat (pmcl->szFile, cszAngle);
#ifdef DEBUG
AuxDebugEx (6, DEBUGLINE TEXT ("appending child %08X; '%s'\r\n"), pti->hItem, pmcl->szFile);
#endif
}
pmcl->bChanges |= MCL_IDF_CHANGED;
}
/*+
*
*-=================================================================*/
STATICFN void LoadDevicesIntoList (
HWND hWnd,
UINT uId,
PMCLASS pmcl,
BOOL bList)
{
HWND hWndT;
TCHAR sz[MAX_ALIAS];
DWORD cch = sizeof(sz) / sizeof(TCHAR);
UINT ii;
BOOL bAdded = FALSE;
hWndT = GetDlgItem (hWnd, uId);
if (!hWndT)
return;
SetWindowRedraw (hWndT, FALSE);
if (bList)
ListBox_ResetContent(hWndT);
else
ComboBox_ResetContent(hWndT);
if (!pmcl->hkMidi &&
RegCreateKey (HKEY_LOCAL_MACHINE, cszDriversRoot, &pmcl->hkMidi))
return;
for (cch = sizeof(sz)/sizeof(TCHAR), ii = 0; ! RegEnumKey (pmcl->hkMidi, ii, sz, cch); ++ii)
{
TCHAR szAlias[MAX_ALIAS];
int ix;
BOOL bExtern;
BOOL bActive;
// read in the friendly name for this driver
//
if (GetAlias (pmcl->hkMidi, sz, szAlias, sizeof(szAlias)/sizeof(TCHAR), &bExtern, &bActive))
continue;
if (IsPrefix (sz, pmcl->pszKey, TEXT('\\')))
pmcl->ixDevice = ii;
// ignore if this is not an external device or if it is disabled
//
if ( ! bExtern || ! bActive)
continue;
// otherwise, add the driver name to the combobox/list
//
if (bList)
{
ix = ListBox_AddString (hWndT, szAlias);
if (ix >= 0)
{
ListBox_SetItemData (hWndT, ix, ii);
bAdded = TRUE;
}
}
else
{
ix = ComboBox_AddString (hWndT, szAlias);
if (ix >= 0)
{
ComboBox_SetItemData (hWndT, ix, ii);
bAdded = TRUE;
}
}
}
SetWindowRedraw (hWndT, TRUE);
EnableWindow (hWndT, bAdded);
if (ii > 0)
InvalidateRect (hWndT, NULL, TRUE);
// iterate back through the items and select the one
// that has item data that corresponds to driver that
// owns the current device
//
if (bList)
{
UINT jj;
for (jj = 0; jj < ii; ++jj)
{
if ((UINT)ListBox_GetItemData (hWndT, jj) == pmcl->ixDevice)
{
ListBox_SetCurSel (hWndT, jj);
break;
}
}
if (jj >= ii)
ListBox_SetCurSel (hWndT, 0);
}
else
{
UINT jj;
for (jj = 0; jj < ii ; ++jj)
{
if ((UINT)ComboBox_GetItemData (hWndT, jj) == pmcl->ixDevice)
{
ComboBox_SetCurSel (hWndT, jj);
break;
}
}
if (jj >= ii)
ComboBox_SetCurSel (hWndT, 0);
}
}
/*+ LoadClass
*
*-=================================================================*/
STATICFN BOOL WINAPI LoadClass (
HWND hWnd,
PMCLASS pmcl)
{
HKEY hKeyA = NULL;
BOOL bRet = FALSE;
UINT cbSize;
UINT cch;
DWORD dw;
if (!pmcl->hkMidi &&
RegCreateKey (HKEY_LOCAL_MACHINE, cszDriversRoot, &pmcl->hkMidi))
goto cleanup;
if (RegOpenKey (pmcl->hkMidi, pmcl->pszKey, &hKeyA))
goto cleanup;
// read data from this key
//
cbSize = sizeof(pmcl->szFile);
RegQueryValueEx (hKeyA, cszDefinition, NULL, &dw, (LPBYTE)pmcl->szFile, &cbSize);
// strip off leading directory (if there is one).
//
cch = lstrlen(pmcl->szFile);
while (cch && (pmcl->szFile[cch-1] != TEXT('\\')))
--cch;
if (cch)
{
TCHAR szFile[MAX_PATH];
lstrcpy (szFile, pmcl->szFile + cch);
lstrcpy (pmcl->szFile, szFile);
}
// get scheme alias
//
cbSize = sizeof(pmcl->szAlias);
RegQueryValueEx (hKeyA, cszFriendlyName, NULL, &dw, (LPBYTE)pmcl->szAlias, &cbSize);
//
//
pmcl->nPort = 0;
cbSize = sizeof(pmcl->nPort);
RegQueryValueEx (hKeyA, cszPort, NULL, &dw, (LPVOID)&pmcl->nPort, &cbSize);
pmcl->bChanges = 0;
bRet = TRUE;
cleanup:
if (hKeyA)
RegCloseKey (hKeyA);
return bRet;
}
/*+ RebuildSchemes
*
* correct key references in the midi schemes when an instrument
* is moved from one external midi port to another
*
*-=================================================================*/
STATICFN BOOL WINAPI RebuildSchemes (
LPTSTR pszOldKey,
LPTSTR pszNewKey)
{
HKEY hkSchemes;
UINT ii;
TCHAR sz[MAX_ALIAS];
UINT cchNew;
cchNew = 0;
if (pszNewKey)
cchNew = lstrlen(pszNewKey) + 1;
#ifdef DEBUG
AuxDebugEx (3, DEBUGLINE TEXT ("RebuildSchemes('%s','%s')\r\n"),
pszOldKey, pszNewKey ? pszNewKey : TEXT ("NULL"));
#endif
if (RegCreateKey (HKEY_LOCAL_MACHINE, cszSchemeRoot, &hkSchemes))
return FALSE;
for (ii = 0; ! RegEnumKey (hkSchemes, ii, sz, sizeof(sz)/sizeof(TCHAR)); ++ii)
{
HKEY hKeyA;
UINT jj;
if (RegOpenKey (hkSchemes, sz, &hKeyA))
continue;
for (jj = 0; ! RegEnumKey (hKeyA, jj, sz, sizeof(sz)/sizeof(TCHAR)); ++jj)
{
UINT cb;
TCHAR szKey[MAX_PATH];
cb = sizeof(szKey);
if (RegQueryValue (hKeyA, sz, szKey, &cb))
continue;
if (IsSzEqual(pszOldKey, szKey))
{
if (cchNew)
RegSetValue (hKeyA, sz, REG_SZ, pszNewKey, cchNew);
else
RegDeleteKey (hKeyA, sz);
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT ("RebuildSchemes - fixing %d\\%d\r\n"), ii, jj);
#endif
}
}
}
return TRUE;
}
/*+ OpenInstrumentKey
*
*-=================================================================*/
STATICFN HKEY WINAPI OpenInstrumentKey (
HWND hWnd,
PMCLASS pmcl,
BOOL bCreate) // create an new key (do not remove or rebuild existing)
{
TCHAR szKey[MAX_ALIAS];
HKEY hkInst;
HKEY hKeyA = NULL;
ZeroMemory (szKey, sizeof (szKey));
#ifdef DEBUG
AuxDebugEx (5, DEBUGLINE TEXT ("OpenInstrumentKey(%X,%08X,%d) szKey=%s\r\n"),
hWnd, pmcl, bCreate, pmcl->pszKey ? pmcl->pszKey : TEXT ("NULL"));
#endif
hkInst = NULL;
if (!pmcl->hkMidi &&
RegCreateKey (HKEY_LOCAL_MACHINE, cszDriversRoot, &pmcl->hkMidi))
goto cleanup;
if (RegEnumKey (pmcl->hkMidi, pmcl->ixDevice, szKey, sizeof(szKey)/sizeof(TCHAR)))
{
assert3(0, TEXT ("Failed to enum Midi device %d"), pmcl->ixDevice);
goto cleanup;
}
#ifdef DEBUG
AuxDebugEx (6, DEBUGLINE TEXT ("ixDevice = %d, Key is %s\r\n"),
pmcl->ixDevice, szKey);
#endif
// if this is a driver key, or if we are not creating and
// the instrument has not changed parentage, we can just
// open the existing key and update its content
//
if (!pmcl->bRemote ||
(!bCreate && IsPrefix (szKey, pmcl->pszKey, TEXT('\\'))))
{
if (RegOpenKey (pmcl->hkMidi, pmcl->pszKey, &hKeyA))
goto cleanup;
#ifdef DEBUG
AuxDebugEx (6, DEBUGLINE TEXT ("opened key %s\r\n"), pmcl->pszKey);
#endif
}
else
{
UINT kk;
TCHAR szEnum[10];
pmcl->bChanges |= MCL_TREE_CHANGED;
lstrcat (szKey, cszSlashInstruments);
if (RegCreateKey (pmcl->hkMidi, szKey, &hkInst))
goto cleanup;
// find an unused keyname
//
for (kk = 0; kk < 128; ++kk)
{
wsprintf (szEnum, csz02d, kk);
if (RegOpenKey (hkInst, szEnum, &hKeyA))
break;
RegCloseKey (hKeyA);
}
lstrcat (szKey, cszSlash);
lstrcat (szKey, szEnum);
// create a key with that name
//
if (RegCreateKey (hkInst, szEnum, &hKeyA))
goto cleanup;
#ifdef DEBUG
AuxDebugEx (6, DEBUGLINE TEXT ("created key %s\r\n"), szKey);
#endif
// we are moving an instrument from one
// external midi port to another
//
if (!bCreate)
{
#ifdef DEBUG
AuxDebugEx (3, DEBUGLINE TEXT ("Deleting key midi\\%s\r\n"), pmcl->pszKey);
#endif
RegDeleteKey (pmcl->hkMidi, pmcl->pszKey);
RebuildSchemes (pmcl->pszKey, szKey);
}
lstrcpy (pmcl->pszKey, szKey);
}
cleanup:
if (hkInst)
RegCloseKey (hkInst);
return hKeyA;
}
/*+ SaveDetails
*
*-=================================================================*/
STATICFN UINT WINAPI SaveDetails (
HWND hWnd,
PMCLASS pmcl,
BOOL bCreate)
{
HWND hWndT;
HKEY hKeyA;
UINT bChanges;
UINT cbSize;
// this should only be called on shutdown
// of details page (or on exit of wizard)
//
assert (pmcl->bDetails);
hKeyA = OpenInstrumentKey (hWnd, pmcl, bCreate);
if ( ! hKeyA)
return FALSE;
hWndT = GetDlgItem (hWnd, IDE_ALIAS);
if (hWndT)
{
TCHAR sz[NUMELMS(pmcl->szAlias)];
GetWindowText (hWndT, sz, NUMELMS(sz));
if ( ! IsSzEqual(sz, pmcl->szAlias))
{
lstrcpy (pmcl->szAlias, sz);
pmcl->bChanges |= MCL_ALIAS_CHANGED;
}
}
#ifdef DEBUG
AuxDebugEx (2, DEBUGLINE TEXT ("--------SaveInstrument---------\r\n"));
AuxDebugEx (2, TEXT ("\tChanges=%x\r\n"), pmcl->bChanges);
AuxDebugEx (2, TEXT ("\tFriendly='%s'\r\n"), pmcl->szAlias);
AuxDebugEx (2, TEXT ("\tDefinition='%s'\r\n"), pmcl->szFile);
#endif
// save value data from this key
//
cbSize = (lstrlen(pmcl->szFile)+1) * sizeof(TCHAR);
RegSetValueEx (hKeyA, cszDefinition, 0, REG_SZ, (LPBYTE)pmcl->szFile,
cbSize);
cbSize = (lstrlen(pmcl->szAlias)+1) * sizeof(TCHAR);
RegSetValueEx (hKeyA, cszFriendlyName, 0, REG_SZ,
(LPBYTE)pmcl->szAlias, cbSize);
RegSetValueEx (hKeyA, cszPort, 0, REG_BINARY, (LPVOID)&pmcl->nPort, 1);
RegCloseKey (hKeyA);
bChanges = pmcl->bChanges;
pmcl->bChanges = 0;
// return 'changed' flag
//
return bChanges;
}
/*+ ParseAngleBrackets
*
* replace '<>' delimiters with 0s and return a pointer
* to the delimited string. This function does nothing if
* the string does not end in a '>' delimiter
*
*-=================================================================*/
static LPTSTR __inline WINAPI ParseAngleBrackets (
LPTSTR pszArg)
{
LPTSTR psz = pszArg + lstrlen(pszArg);
while (--psz > pszArg)
{
if (*psz == TEXT('>'))
{
*psz = 0;
while (--psz >= pszArg)
{
if (*psz == TEXT('<'))
{
*psz = 0;
return psz+1;
}
}
}
}
return NULL;
}
/*+ fnFindDevice
*
*-=================================================================*/
struct _find_data {
HWND hWnd;
UINT idMfg;
UINT idProd;
LPTSTR pszInstr;
};
STATICFN BOOL WINAPI fnFindDevice (
LPVOID lpv,
UINT nEnum,
LPIDFHEADER pHdr,
LPIDFINSTINFO pInst)
{
struct _find_data * pfd = lpv;
TCHAR szTemp[MAX_PATH];
assert (pfd);
MultiByteToWideChar(GetACP(), 0,
pHdr->abInstID, -1,
szTemp, sizeof(szTemp)/sizeof(TCHAR));
if (!pfd->pszInstr ||
IsSzEqual (pfd->pszInstr, szTemp))
{
if (SetDlgItemText (pfd->hWnd, pfd->idMfg, (TCHAR*)(pInst->abData+pInst->cbManufactASCII )))
pfd->idMfg = 0;
if (SetDlgItemText (pfd->hWnd, pfd->idProd,
(TCHAR*)(pInst->abData
+ pInst->cbManufactASCII + pInst->cbManufactUNICODE)))
pfd->idProd = 0;
// we can stop enumerating now
//
return FALSE;
}
// return true to consider ennumeration
//
return TRUE;
}
/*+ ActivateInstrumentPage
*
*-=================================================================*/
STATICFN void WINAPI ActivateInstrumentPage (
HWND hWnd,
PMCLASS pmcl)
{
pmcl->bDetails = FALSE;
if (GetDlgItem (hWnd, IDC_TYPES))
{
pmcl->bDetails = TRUE;
LoadTypesIntoTree (hWnd, IDC_TYPES, pmcl);
SetTypesEdit (hWnd, IDE_TYPES, pmcl);
LoadDevicesIntoList (hWnd, IDC_DEVICES, pmcl, FALSE);
if ( ! pmcl->bRemote)
{
HWND hWndT = GetDlgItem (hWnd, IDC_DEVICES);
if (hWndT)
EnableWindow (hWndT, FALSE);
}
}
else
{
struct _find_data fd;
TCHAR szFile[NUMELMS(pmcl->szFile)];
if ( ! IsFullPath (pmcl->szFile))
{
UINT cch;
GetIDFDirectory (szFile, NUMELMS(szFile));
cch = lstrlen (szFile);
if (cch && szFile[cch-1] != TEXT('\\'))
szFile[cch++] = TEXT('\\');
lstrcpyn (szFile + cch, pmcl->szFile, NUMELMS(szFile)-cch);
}
else
lstrcpy (szFile, pmcl->szFile);
fd.hWnd = hWnd;
fd.idMfg = IDC_MANUFACTURER;
fd.idProd = IDC_DEVICE_TYPE;
fd.pszInstr = ParseAngleBrackets(szFile);
idfEnumInstruments (szFile, fnFindDevice, &fd);
if (fd.idMfg)
SetDlgItemText (hWnd, fd.idMfg, cszEmpty);
if (fd.idProd)
{
LoadString (ghInstance, IDS_UNSPECIFIED, szFile, NUMELMS(szFile));
SetDlgItemText (hWnd, fd.idProd, szFile);
}
}
}
/*+ IsInstrumentKey
*
* return TRUE if the keyname passed refers to an instrument key
* rather than a device key. device keys usually end in '>',
* while instrument keys will always be of the form
* <dev>\Instruments\<enum> where <dev> and <enum> can be arbitrary
* strings.
*
*-=================================================================*/
STATICFN BOOL WINAPI IsInstrumentKey (
LPTSTR pszKey)
{
UINT cch = lstrlen(pszKey);
if (!cch)
return FALSE;
if (pszKey[cch-1] == TEXT('>'))
return FALSE;
while (--cch)
if (pszKey[cch] == TEXT('\\'))
return TRUE;
return FALSE;
}
/*+ InitInstrumentProps
*
*-=================================================================*/
STATICFN BOOL WINAPI InitInstrumentProps (
HWND hWnd,
PMCLASS pmcl)
{
LPPROPSHEETPAGE ppsp = pmcl->ppsp;
PMPSARGS pmpsa;
assert (ppsp && ppsp->dwSize == sizeof(*ppsp));
if (!ppsp)
return FALSE; // EndDialog (hWnd, FALSE);
pmcl->bRemote = FALSE;
pmpsa = (LPVOID)ppsp->lParam;
if (pmpsa && pmpsa->lpfnMMExtPSCallback)
{
pmpsa->lpfnMMExtPSCallback (MM_EPS_GETNODEDESC,
(DWORD_PTR)pmcl->szAlias,
sizeof(pmcl->szAlias),
(DWORD_PTR)pmpsa->lParam);
#ifdef DEBUG
AuxDebugEx (3, TEXT ("\tgot szAlias='%s'\r\n"), pmcl->szAlias);
#endif
pmpsa->lpfnMMExtPSCallback (MM_EPS_GETNODEID,
(DWORD_PTR)pmcl->szFullKey,
sizeof(pmcl->szFullKey),
(DWORD_PTR)pmpsa->lParam);
#ifdef DEBUG
AuxDebugEx (3, TEXT ("\tgot szFullKey='%s'\r\n"), pmcl->szFullKey);
#endif
// skip over the midi\ part of the key if we have been
// passed that. we want the driver name to be the first
// part of the key
//
pmcl->pszKey = pmcl->szFullKey;
if (!lstrnicmp (pmcl->pszKey,
(LPTSTR)cszMidiSlash,
lstrlen(cszMidiSlash)))
{
pmcl->pszKey += lstrlen(cszMidiSlash);
}
// If this is an instrument key, set bRemote to true
//
if (IsInstrumentKey(pmcl->pszKey))
pmcl->bRemote = TRUE;
}
else
LoadString (ghInstance, IDS_UNSPECIFIED,
pmcl->szAlias, NUMELMS(pmcl->szAlias));
SetDlgItemText (hWnd, IDE_ALIAS, pmcl->szAlias);
Static_SetIcon(GetDlgItem (hWnd, IDC_CLASS_ICON),
LoadIcon (ghInstance, MAKEINTRESOURCE(IDI_INSTRUMENT)));
LoadClass (hWnd, pmcl);
//ActivateInstrumentPage(hWnd, pmcl);
return TRUE;
}
/*+ NotifyMapper
*
*-=================================================================*/
STATICFN void WINAPI NotifyMapper (
PMCLASS pmcl,
UINT bChanges,
HWND hWnd)
{
// tell midi mapper about tree changes, IDF changes and port changes
//
if (bChanges & (MCL_TREE_CHANGED | MCL_IDF_CHANGED | MCL_PORT_CHANGED))
{
KickMapper (hWnd);
}
}
/*+
*
*-=================================================================*/
STATICFN BOOL WINAPI RemoveInstrument (
HWND hWnd,
PMCLASS pmcl)
{
RegDeleteKey (pmcl->hkMidi, pmcl->pszKey);
RebuildSchemes (pmcl->pszKey, NULL);
return TRUE;
}
BOOL WINAPI RemoveInstrumentByKeyName (
LPCTSTR pszKey)
{
MCLASS mcl;
BOOL rc = FALSE;
memset ((TCHAR *)&mcl, 0x00, sizeof(mcl));
mcl.pszKey = (LPTSTR)pszKey;
if (!lstrnicmp (mcl.pszKey,
(LPTSTR)cszMidiSlash,
lstrlen(cszMidiSlash)))
{
mcl.pszKey += lstrlen(cszMidiSlash);
}
if (LoadClass (NULL, &mcl))
{
rc = RemoveInstrument (NULL, &mcl);
if (mcl.hkMidi)
RegCloseKey (mcl.hkMidi);
}
return rc;
}
/*+ MidiInstrumentCommands
*
*-=================================================================*/
BOOL WINAPI MidiInstrumentCommands (
HWND hWnd,
UINT_PTR uId,
LPNMHDR lpnm)
{
PMCLASS pmcl = GetDlgData(hWnd);
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT ("InstrumentCommands(..%d..) %d(%xx)\r\n"),
uId, lpnm->code, lpnm->code);
#endif
if (!pmcl)
return FALSE;
switch (uId)
{
case IDE_ALIAS:
if (lpnm->code == EN_CHANGE)
PropSheet_Changed(GetParent(hWnd), hWnd);
break;
case IDB_REMOVE:
if (RemoveInstrument (hWnd, pmcl))
{
PMPSARGS pmpsa = (LPVOID)pmcl->ppsp->lParam;
if (pmpsa && pmpsa->lpfnMMExtPSCallback)
pmpsa->lpfnMMExtPSCallback (MM_EPS_TREECHANGE, 0, 0, (DWORD_PTR)pmpsa->lParam);
NotifyMapper (pmcl, MCL_TREE_CHANGED, hWnd);
SetDlgData(hWnd, NULL);
if (pmcl->hkMidi)
RegCloseKey (pmcl->hkMidi), pmcl->hkMidi = NULL;
LocalFree ((HLOCAL)(UINT_PTR)(DWORD_PTR)pmcl);
PropSheet_PressButton(GetParent(hWnd), PSBTN_CANCEL);
}
break;
case IDB_NEWTYPE:
InstallNewIDF (hWnd);
LoadTypesIntoTree (hWnd, IDC_TYPES, pmcl);
SetTypesEdit (hWnd, IDE_TYPES, pmcl);
break;
case IDC_TYPES:
if ((lpnm->code == TVN_SELCHANGED) && !pmcl->bFillingList)
{
HandleTypesSelChange (pmcl, lpnm);
SetTypesEdit (hWnd, IDE_TYPES, pmcl);
PropSheet_Changed(GetParent(hWnd), hWnd);
#ifdef DEBUG
AuxDebugEx (5, DEBUGLINE TEXT ("file='%s'\r\n"), pmcl->szFile);
#endif
}
break;
case IDC_DEVICES:
if (lpnm->code == CBN_SELCHANGE)
{
int ix = ComboBox_GetCurSel (lpnm->hwndFrom);
pmcl->ixDevice = (UINT) ((ix >= 0) ? ComboBox_GetItemData (lpnm->hwndFrom, ix) : -1);
PropSheet_Changed(GetParent(hWnd), hWnd);
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT ("IDC_DEVICES.selChange(%d) %d\r\n"), ix, pmcl->ixDevice);
#endif
}
break;
// we get these only if invoked as a dialog, not as a property
// sheet
//
case IDOK:
{
UINT bChanges = SaveDetails (hWnd, pmcl, FALSE);
NotifyMapper (pmcl, bChanges, hWnd);
}
// fall through
case IDCANCEL:
EndDialog (hWnd, uId);
break;
case 0:
{
LONG lRet = FALSE;
switch (lpnm->code)
{
case PSN_APPLY:
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT ("ID_APPLY\r\n"));
#endif
if (pmcl->bDetails)
{
UINT bChanges = SaveDetails (hWnd, pmcl, FALSE);
NotifyMapper (pmcl, bChanges, hWnd);
// tell mmsys.cpl about tree & alias changes
//
if (bChanges & (MCL_TREE_CHANGED | MCL_ALIAS_CHANGED))
{
PMPSARGS pmpsa = (LPVOID)pmcl->ppsp->lParam;
if (pmpsa && pmpsa->lpfnMMExtPSCallback)
pmpsa->lpfnMMExtPSCallback (MM_EPS_TREECHANGE, 0, 0, (DWORD_PTR)pmpsa->lParam);
}
// we do this because the SysTreeView for IDF files
// forgets its selection when APPLY is pressed. go figure
//
#ifdef DEBUG
AuxDebugEx (7, DEBUGLINE TEXT ("PSN_APPLY: re-doing selection '%s'\r\n"), pmcl->szFile);
//ActivateInstrumentPage (hWnd, pmcl);
AuxDebugEx (7, DEBUGLINE TEXT ("PSN_APPLY: done re-doing selection '%s'\r\n"), pmcl->szFile);
#endif
}
break;
case PSN_KILLACTIVE:
break;
case PSN_SETACTIVE:
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT ("PSN_SETACTIVE\r\n"));
#endif
ActivateInstrumentPage (hWnd, pmcl);
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT ("PSN_SETACTIVE ends\r\n"));
#endif
break;
}
SetWindowLongPtr (hWnd, DWLP_MSGRESULT, (LONG_PTR)lRet);
break;
}
}
return FALSE;
}
const static DWORD aKeyWordIds[] = { // Context Help IDs
IDC_CLASS_ICON, IDH_MMCPL_DEVPROP_DETAILS_INSTRUMENT,
IDE_ALIAS, IDH_MMCPL_DEVPROP_DETAILS_INSTRUMENT,
IDC_DEVICES, IDH_MMCPL_DEVPROP_DETAILS_MIDI_PORT,
IDC_TYPES, IDH_MMCPL_DEVPROP_DETAILS_INS_DEF,
IDB_NEWTYPE, IDH_MMCPL_DEVPROP_DETAILS_BROWSE,
0, 0
};
/*+ MidiInstrumentDlgProc
*
*-=================================================================*/
INT_PTR CALLBACK MidiInstrumentDlgProc (
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
#if defined DEBUG || defined DEBUG_RETAIL
TCHAR chNest = szNestLevel[0]++;
#endif
switch (uMsg)
{
case WM_COMMAND:
{
NMHDR nmh;
nmh.hwndFrom = GET_WM_COMMAND_HWND(wParam, lParam);
nmh.idFrom = GET_WM_COMMAND_ID(wParam, lParam);
nmh.code = GET_WM_COMMAND_CMD(wParam, lParam);
MidiInstrumentCommands(hWnd, nmh.idFrom, &nmh);
}
break;
case WM_NOTIFY:
#ifdef DEBUG
AuxDebugEx (3, DEBUGLINE TEXT ("WM_NOTIFY(%x,%x,%x)\r\n"), hWnd, wParam, lParam);
#endif
#if defined DEBUG || defined DEBUG_RETAIL
++szNestLevel[0];
#endif
MidiInstrumentCommands(hWnd, wParam, (LPVOID)lParam);
#if defined DEBUG || defined DEBUG_RETAIL
--szNestLevel[0];
#endif
break;
case WM_INITDIALOG:
{
PMCLASS pmcl;
pmcl = (LPVOID)LocalAlloc(LPTR, sizeof(*pmcl));
if (!pmcl)
{
EndDialog(hWnd, FALSE);
break;
}
pmcl->ppsp = (LPVOID)lParam;
SetDlgData (hWnd, pmcl);
#ifdef DEBUG
AuxDebugEx (5, DEBUGLINE TEXT ("midiInstrument.WM_INITDLG ppsp=%08X\r\n"));
#endif
//AuxDebugDump (8, pmcl->ppsp, sizeof(*(pmcl->ppsp)));
InitInstrumentProps (hWnd, pmcl);
break;
}
case WM_DESTROY:
{
PMCLASS pmcl = GetDlgData(hWnd);
if (pmcl)
{
if (pmcl->hkMidi)
RegCloseKey (pmcl->hkMidi), pmcl->hkMidi = NULL;
#ifdef USE_IDF_ICONS
if (pmcl->hIDFImageList)
{
HWND hWndT = GetDlgItem (hWnd, IDC_TYPES);
if (hWndT)
TreeView_SetImageList (hWndT, NULL, TVSIL_NORMAL);
ImageList_Destroy (pmcl->hIDFImageList);
pmcl->hIDFImageList = NULL;
}
#endif
LocalFree ((HLOCAL)(UINT_PTR)(DWORD_PTR)pmcl);
}
break;
}
case WM_CONTEXTMENU:
WinHelp ((HWND) wParam, NULL, HELP_CONTEXTMENU,
(UINT_PTR) (LPTSTR) aKeyWordIds);
break;
case WM_HELP:
{
LPHELPINFO lphi = (LPVOID) lParam;
WinHelp (lphi->hItemHandle, NULL, HELP_WM_HELP,
(UINT_PTR) (LPTSTR) aKeyWordIds);
break;
}
}
#if defined DEBUG || defined DEBUG_RETAIL
szNestLevel[0] = chNest;
#endif
return FALSE;
}
/// --------------------- Wizard stuff ----------------------
static LPTSTR aidWiz[] = {
MAKEINTRESOURCE(IDD_MIDIWIZ02),
MAKEINTRESOURCE(IDD_MIDIWIZ03),
MAKEINTRESOURCE(IDD_MIDIWIZ04)
};
#define WIZ_TEMPLATE_DEVICE aidWiz[0]
#define WIZ_TEMPLATE_IDF aidWiz[1]
#define WIZ_TEMPLATE_ALIAS aidWiz[2]
typedef struct _wizdata {
LPPROPSHEETPAGE ppspActive;
HBITMAP hBmp;
MCLASS mcl;
PMCMIDI pmcm;
HPROPSHEETPAGE ahpsp[NUMELMS(aidWiz)];
} WIZDATA, * PWIZDATA;
/*+ FindInstrument
*
*-=================================================================*/
STATICFN PINSTRUM WINAPI FindInstrument (
PMCMIDI pmcm,
LPTSTR pszFriendly)
{
UINT ii;
for (ii = 0; ii < pmcm->nInstr; ++ii)
{
assert (pmcm->api[ii]);
if (IsSzEqual(pszFriendly, pmcm->api[ii]->szFriendly))
return pmcm->api[ii];
}
return NULL;
}
/*+ UniqueFriendlyName
*
*-=================================================================*/
STATICFN BOOL WINAPI fnFirstInstr (
LPVOID lpv,
UINT nEnum,
LPIDFHEADER pHdr,
LPIDFINSTINFO pInst)
{
LPTSTR pszInstr = lpv;
assert (pszInstr);
MultiByteToWideChar(GetACP(), 0,
pHdr->abInstID, -1,
pszInstr, MAX_ALIAS);
return FALSE;
}
STATICFN BOOL WINAPI UniqueFriendlyName (
PMCMIDI pmcm,
PMCLASS pmcl,
LPTSTR pszAlias,
UINT cchAlias)
{
TCHAR szFile[MAX_PATH * 2];
LPTSTR pszInstr;
UINT cch;
UINT ii;
GetIDFDirectory (szFile, sizeof(szFile)/sizeof(TCHAR));
cch = lstrlen(szFile);
if (cch && szFile[cch-1] != TEXT('\\'))
szFile[cch++] = TEXT('\\');
lstrcpy (szFile + cch, pmcl->szFile);
pszInstr = ParseAngleBrackets (szFile);
if ( ! pszInstr)
{
pszInstr = szFile + lstrlen(szFile) + 1;
idfEnumInstruments (szFile, fnFirstInstr, pszInstr);
}
// if no instrument name from the IDF file, get a default
// from our resources
//
if ( ! lstrlen (pszInstr))
{
LoadString (ghInstance, IDS_DEF_INSTRNAME, pszInstr, MAX_ALIAS);
return FALSE;
}
// make the instrument name the same as the alias, and prepare
// to append a number if the alias turns out not to be unique
//
lstrcpyn (pszAlias, pszInstr, cchAlias);
cch = lstrlen (pszAlias);
cch = min (cch, (UINT)MAX_ALIAS-3);
ii = 1;
// loop while we are trying to use an instrument name
// that has already been used
//
while (FindInstrument (pmcm, pszAlias))
{
static CONST TCHAR cszSpaceD[] = TEXT (" %d");
wsprintf (pszAlias + cch, cszSpaceD, ++ii);
if (ii > NUMELMS(pmcm->api))
{
assert2(0, TEXT ("infinite loop in UniqueFriendlyName!"));
break;
}
}
return TRUE;
}
/*+ MidiWizardCommands
*
*-=================================================================*/
BOOL WINAPI MidiWizardCommands (
HWND hWnd,
UINT_PTR uId,
LPNMHDR lpnm)
{
PWIZDATA pwd;
LPPROPSHEETPAGE ppsp = GetDlgData(hWnd);
LONG lRet = TRUE;
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT ("WizardCmd ppsp=%08X code=%d(0x%X)\r\n"),
ppsp, lpnm->code, lpnm->code);
#endif
pwd = NULL;
if (ppsp)
pwd = (LPVOID)ppsp->lParam;
assert (pwd);
switch (uId)
{
case IDC_TYPES:
if ((lpnm->code == TVN_SELCHANGED) && !pwd->mcl.bFillingList)
{
HandleTypesSelChange (&pwd->mcl, lpnm);
UniqueFriendlyName (pwd->pmcm, &pwd->mcl, pwd->mcl.szAlias, NUMELMS(pwd->mcl.szAlias));
#ifdef DEBUG
AuxDebugEx (5, DEBUGLINE TEXT ("file='%s'\r\n"), pwd->mcl.szFile);
#endif
}
break;
case IDB_NEWTYPE:
InstallNewIDF (hWnd);
LoadTypesIntoTree (hWnd, IDC_TYPES, &pwd->mcl);
break;
//case IDC_DEVICES:
// break;
//case IDE_ALIAS:
// break;
case 0:
{
switch (lpnm->code)
{
case PSN_HELP:
break;
case PSN_KILLACTIVE:
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT ("PSN_KILLACTIVE\r\n"));
#endif
break;
case PSN_SETACTIVE:
{
DWORD dwWizBtn;
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT ("PSN_SETACTIVE\r\n"));
#endif
if (pwd)
pwd->ppspActive = ppsp;
if (ppsp->pszTemplate == WIZ_TEMPLATE_DEVICE) // midi device
LoadDevicesIntoList (hWnd, IDC_DEVICES, &pwd->mcl, TRUE);
else if (ppsp->pszTemplate == WIZ_TEMPLATE_IDF) // idf file
LoadTypesIntoTree (hWnd, IDC_TYPES, &pwd->mcl);
else if (ppsp->pszTemplate == WIZ_TEMPLATE_ALIAS) // alias
SetDlgItemText (hWnd, IDE_ALIAS, pwd->mcl.szAlias);
dwWizBtn = PSWIZB_NEXT | PSWIZB_BACK;
if (ppsp->pszTemplate == aidWiz[NUMELMS(aidWiz)-1])
dwWizBtn = PSWIZB_FINISH | PSWIZB_BACK;
else if (ppsp->pszTemplate == aidWiz[0])
dwWizBtn = PSWIZB_NEXT;
PropSheet_SetWizButtons (GetParent(hWnd), dwWizBtn);
}
break;
case PSN_WIZNEXT:
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT ("PSN_WIZNEXT\r\n"));
#endif
if (ppsp->pszTemplate == WIZ_TEMPLATE_DEVICE) // midi device
{
HWND hWndT;
int ix;
pwd->mcl.ixDevice = (UINT)-1;
pwd->mcl.nPort = 0;
hWndT = GetDlgItem (hWnd, IDC_DEVICES);
if (hWndT)
{
ix = ListBox_GetCurSel (hWndT);
if (ix >= 0)
pwd->mcl.ixDevice = (UINT) ListBox_GetItemData (hWndT, ix);
}
if (pwd->mcl.ixDevice == (UINT)-1)
SetWindowLongPtr (hWnd, DWLP_MSGRESULT, (LONG_PTR)-1);
}
else if (ppsp->pszTemplate == WIZ_TEMPLATE_IDF) // idf file
{
if ( ! pwd->mcl.szAlias[0])
{
LoadString (ghInstance, IDS_DEF_INSTRNAME,
pwd->mcl.szAlias,
NUMELMS(pwd->mcl.szAlias));
}
}
else if (ppsp->pszTemplate == WIZ_TEMPLATE_IDF) // alias
{
GetDlgItemText (hWnd, IDE_ALIAS, pwd->mcl.szAlias,
NUMELMS(pwd->mcl.szAlias));
}
break;
case PSN_WIZBACK:
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT ("PSN_WIZBACK\r\n"));
#endif
break;
case PSN_WIZFINISH:
#ifdef DEBUG
AuxDebugEx (4, DEBUGLINE TEXT ("PSN_WIZFINISH\r\n"));
#endif
//if (!save success)
lRet = FALSE;
//SetWindowLong (hWnd, DWL_MSGRESULT, lRet);
SaveDetails (hWnd, &pwd->mcl, TRUE);
break;
default:
lRet = FALSE;
}
}
break;
}
return lRet;
}
/*+ MidiWizardDlgProc
*
*-=================================================================*/
INT_PTR CALLBACK MidiWizardDlgProc (
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
BOOL bRet = TRUE;
#if defined DEBUG || defined DEBUG_RETAIL
TCHAR chNest = szNestLevel[0]++;
#endif
switch (uMsg)
{
case WM_COMMAND:
{
NMHDR nmh;
nmh.hwndFrom = GET_WM_COMMAND_HWND(wParam, lParam);
nmh.idFrom = GET_WM_COMMAND_ID(wParam, lParam);
nmh.code = GET_WM_COMMAND_CMD(wParam, lParam);
bRet = MidiWizardCommands(hWnd, nmh.idFrom, &nmh);
}
break;
case WM_NOTIFY:
#ifdef DEBUG
AuxDebugEx (6, DEBUGLINE TEXT ("WM_NOTIFY(%x,%x,%x)\r\n"), hWnd, wParam, lParam);
#endif
bRet = MidiWizardCommands(hWnd, wParam, (LPVOID)lParam);
break;
case WM_INITDIALOG:
{
PWIZDATA pwd;
LPPROPSHEETPAGE ppsp = (LPVOID)lParam;
SetDlgData (hWnd, lParam);
pwd = (LPVOID)ppsp->lParam;
SendDlgItemMessage(hWnd, IDC_WIZBMP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)pwd->hBmp);
#ifdef DEBUG
AuxDebugEx (5, DEBUGLINE TEXT ("MidiWizard.WM_INITDLG ppsp=%08X\r\n"), ppsp);
#endif
}
break;
case WM_DESTROY:
{
PWIZDATA pwd;
LPPROPSHEETPAGE ppsp = GetDlgData(hWnd);
if (ppsp && (pwd = (LPVOID)ppsp->lParam) != NULL)
{
if (pwd->mcl.hkMidi)
RegCloseKey (pwd->mcl.hkMidi), pwd->mcl.hkMidi = NULL;
#ifdef USE_IDF_ICONS
if (pwd->mcl.hIDFImageList)
{
HWND hWndT = GetDlgItem (hWnd, IDC_TYPES);
if (hWndT)
TreeView_SetImageList (hWndT, NULL, TVSIL_NORMAL);
ImageList_Destroy (pwd->mcl.hIDFImageList);
pwd->mcl.hIDFImageList = NULL;
}
#endif
}
}
break;
default:
bRet = FALSE;
break;
}
#if defined DEBUG || defined DEBUG_RETAIL
szNestLevel[0] = chNest;
#endif
return bRet;
}
INT CALLBACK
iSetupDlgCallback(
IN HWND hwndDlg,
IN UINT uMsg,
IN LPARAM lParam
)
/*++
Routine Description:
Call back used to remove the "?" from the wizard page.
Arguments:
hwndDlg - Handle to the property sheet dialog box.
uMsg - Identifies the message being received. This parameter
is one of the following values:
PSCB_INITIALIZED - Indicates that the property sheet is
being initialized. The lParam value is zero for this message.
PSCB_PRECREATE Indicates that the property sheet is about
to be created. The hwndDlg parameter is NULL and the lParam
parameter is a pointer to a dialog template in memory. This
template is in the form of a DLGTEMPLATE structure followed
by one or more DLGITEMTEMPLATE structures.
lParam - Specifies additional information about the message. The
meaning of this value depends on the uMsg parameter.
Return Value:
The function returns zero.
--*/
{
switch( uMsg )
{
case PSCB_INITIALIZED:
break;
case PSCB_PRECREATE:
if( lParam ){
DLGTEMPLATE *pDlgTemplate = (DLGTEMPLATE *)lParam;
pDlgTemplate->style &= ~DS_CONTEXTHELP;
}
break;
}
return FALSE;
}
/*+ MidiInstrumentsWizard
*
*-=================================================================*/
INT_PTR MidiInstrumentsWizard (
HWND hWnd,
PMCMIDI pmcm, // optional
LPTSTR pszDriverKey) // optional
{
WIZDATA wd;
PROPSHEETHEADER psh;
PROPSHEETPAGE psp;
UINT ii;
INT_PTR iRet = -1;
LPTSTR psz;
ZeroMemory (&wd, sizeof(wd));
wd.mcl.bDetails = TRUE;
wd.mcl.bRemote = TRUE;
wd.mcl.ixDevice = 0;
LoadString (ghInstance, IDS_DEF_DEFINITION, wd.mcl.szFile,
NUMELMS(wd.mcl.szFile));
// set the default driver key to what was passed.
// If someone passed us a path, rather than a driver key
// null out the '\\' characters so that we see only the
// leading driver part of the key.
//
wd.mcl.pszKey = wd.mcl.szFullKey;
if (pszDriverKey)
lstrcpy (wd.mcl.szFullKey, pszDriverKey);
if (!lstrnicmp (wd.mcl.pszKey, (LPTSTR)cszMidiSlash, lstrlen(cszMidiSlash)))
wd.mcl.pszKey += lstrlen(cszMidiSlash);
psz = wd.mcl.pszKey;
while (*psz)
{
if (*psz == TEXT('\\'))
*psz = 0;
++psz;
}
// load all current instrument names from the registry
//
if (!(wd.pmcm = pmcm))
{
wd.pmcm = (LPVOID) LocalAlloc (LPTR, sizeof(MCMIDI));
if (!wd.pmcm) return -1;
LoadInstruments (wd.pmcm, FALSE);
}
psp.dwSize = sizeof(psp);
psp.dwFlags = PSP_DEFAULT;
psp.hInstance = ghInstance;
psp.pfnDlgProc = MidiWizardDlgProc;
psp.lParam = (LPARAM)&wd;
for (psh.nPages = 0, ii = 0; ii < NUMELMS(aidWiz); ++ii)
{
HPROPSHEETPAGE hpsp;
psp.pszTemplate = aidWiz[ii];
wd.ahpsp[psh.nPages] = hpsp = CreatePropertySheetPage(&psp);
if (hpsp)
++psh.nPages;
}
if ( ! psh.nPages)
return -1;
wd.hBmp = LoadBitmap(ghInstance, MAKEINTRESOURCE(IDB_WIZBMP));
#ifdef DEBUG
AuxDebugEx (3, DEBUGLINE TEXT ("Wizard bitmap = %08X\r\n"));
#endif
psh.dwSize = sizeof(psh);
psh.dwFlags = PSH_PROPTITLE | PSH_WIZARD_LITE | PSH_USECALLBACK;
psh.hwndParent = hWnd;
psh.hInstance = ghInstance;
psh.pszCaption = MAKEINTRESOURCE(IDS_WIZNAME);
psh.nPages = NUMELMS(aidWiz);
psh.nStartPage = 0;
psh.phpage = wd.ahpsp;
psh.pfnCallback = iSetupDlgCallback;
iRet = PropertySheet (&psh);
// free dynamically allocated stuff.
//
if (wd.hBmp)
DeleteObject (wd.hBmp);
// if no MCMIDI was passed, we dynamically loaded one,
// so now we need to free it.
//
if ( ! pmcm)
{
if (wd.pmcm->hkMidi)
RegCloseKey (wd.pmcm->hkMidi);
FreeInstruments (wd.pmcm);
LocalFree ((HLOCAL)(UINT_PTR)(DWORD_PTR)wd.pmcm);
}
return iRet;
}