You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2318 lines
63 KiB
2318 lines
63 KiB
/*==========================================================================*/
|
|
//
|
|
// 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, ARRAYSIZE(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;
|
|
}
|