/* File: Advanced.cpp Project: Joystick Control Panel OLE Client Author: Brycej Date: 02/07/97 Comments: Window proc for Avanced page in cpanel Copyright (c) 1997, Microsoft Corporation */ // This is necessary LVS_EX_INFOTIP /* #if (_WIN32_IE < 0x0500) #undef _WIN32_IE #define _WIN32_IE 0x0500 #endif */ #include #include #include #include // For RegisterDeviceNotification stuff! #include // for DBT_ defines!!! #include "cpanel.h" #include "hsvrguid.h" #include "resource.h" #include "joyarray.h" // MyListCtrl prototypes #include "inplace.h" #define USE_DEFAULT 0x1000 // If this bit is set, the device is going to use GCDEF! #define SHOW_DEFAULT 0x2000 // Show default check box if clsidConfig is != CLSID_LegacyServer // constants const short NO_ITEM = -1; #define DEVICE_ID 0 #define DEVICE_FRIENDLY 1 #define DEVICE_TYPE 2 #define DEVICE_PORT 3 LPCWSTR lpMSANALOG_VXD = L"MSANALOG.VXD"; LPTSTR lpstrNone; #define ADVANCED_ID_COLUMN 0 #define ADVANCED_DEVICE_COLUMN 1 extern const DWORD gaHelpIDs[]; // externs for arguements! extern BYTE nID, nStartPageDef, nStartPageCPL; // Update flag! extern short nFlags; // local (module-scope) variables HWND hAdvListCtrl; #ifdef _UNICODE static PVOID hAdvNotifyDevNode; #endif extern short iItem; static HWND ghDlg; //static UINT JoyCfgChangedMsg; static BOOL bProcess; // Message Procedures for handling VK_DELETE in Advanced window static WNDPROC fpMainWndProc; static BOOL WINAPI SubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); // Message Procedures for handling the VK_ENTER/VK_DELETE in Adv Window static WNDPROC fpPageWndProc; static BOOL WINAPI KeySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); short iAdvItem = NO_ITEM; // index of selected item char iGameportDriverItem = NO_ITEM; short nOldID; // externs extern IDirectInputJoyConfig *pDIJoyConfig; extern LPDIRECTINPUT lpDIInterface; extern BYTE nGameportBus; extern PJOY pAssigned[MAX_ASSIGNED]; // List of assigned devices extern BYTE nAssigned; // Number of elements in pAssigned array extern HINSTANCE ghInstance; #ifdef WINNT // external function defined in CPANEL.CPP extern void RunWDMJOY ( void ); #endif // local message handlers static BOOL OnInitDialog (HWND, HWND, LPARAM); static void OnCommand (HWND, int, HWND, UINT); static BOOL OnNotify (HWND, WPARAM, LPNMHDR); static void OnDestroy (HWND); static void OnAdvHelp (LPARAM); static void OnContextMenu (WPARAM wParam, LPARAM lParam); static void OnListviewContextMenu ( LPARAM lParam ); // local utility fns static BOOL SetActiveGlobalDriver ( void ); static BOOL AdvUpdateListCtrl ( void ); static BOOL UpdateChangeListCtrl ( HWND hCtrl ); #ifndef _UNICODE static void PopulateGlobalPortDriverComboBox( void ); extern WCHAR *pwszGameportDriverArray[MAX_GLOBAL_PORT_DRIVERS]; extern BYTE nGameportDriver; // Global Port Driver Enumeration Counter #define POLL_FLAGS_REG_STR TEXT("PollFlags") #endif static void LaunchChange ( HWND hTmp ); int CALLBACK CompareIDItems (LPARAM item1, LPARAM item2, LPARAM uDirection); void EditSubLabel( BYTE nItem, BYTE nCol ); /////////////////////////////////////////////////////////////////////////////// // FUNCTION: AdvancedProc(HWND hDlg, ULONG uMsg, WPARAM wParam, LPARAM lParam) // // PARAMETERS: hDlg - // uMsg - // wParam - // lParam - // // PURPOSE: Main callback function for "Advanced" sheet /////////////////////////////////////////////////////////////////////////////// BOOL WINAPI AdvancedProc(HWND hDlg, ULONG uMsg, WPARAM wParam, LPARAM lParam) { switch( uMsg ) { case WM_ACTIVATEAPP: if( wParam ) SetListCtrlItemFocus(hAdvListCtrl, (BYTE)iAdvItem); break; case WM_DEVICECHANGE: switch( (UINT)wParam ) { case DBT_DEVICEARRIVAL: // case DBT_DEVICEREMOVECOMPLETE: // Clear the old "known devices" list nFlags |= UPDATE_ALL; // Clear pAssigned while( nAssigned ) { if( pAssigned[--nAssigned] ) { delete[] (pAssigned[nAssigned]); pAssigned[nAssigned] = 0; } } // Rebuild the "known devices" list - pAssigned lpDIInterface->EnumDevices(DIDEVTYPE_JOYSTICK, (LPDIENUMDEVICESCALLBACK)DIEnumDevicesProc, (LPVOID)hDlg, DIEDFL_ALLDEVICES); AdvUpdateListCtrl(); break; } break; case WM_LBUTTONDOWN: // Click Drag service for PropSheets! PostMessage(GetParent(hDlg), WM_NCLBUTTONDOWN, (WPARAM)HTCAPTION, lParam); break; case WM_INITDIALOG: if( !HANDLE_WM_INITDIALOG(hDlg, wParam, lParam, OnInitDialog) ) { // Fix #108983 NT, Remove Flash on Error condition. SetWindowPos(::GetParent(hDlg), HWND_BOTTOM, 0, 0, 0, 0, SWP_HIDEWINDOW); DestroyWindow(hDlg); } return(TRUE); case WM_COMMAND: HANDLE_WM_COMMAND(hDlg, wParam, lParam, OnCommand); return(TRUE); case WM_DESTROY: return(HANDLE_WM_DESTROY(hDlg, wParam, lParam, OnDestroy)); case WM_NOTIFY: return(HANDLE_WM_NOTIFY(hDlg, wParam, lParam, OnNotify)); case WM_HELP: OnAdvHelp(lParam); return(TRUE); case WM_CONTEXTMENU: OnContextMenu(wParam, lParam); return(TRUE); default: break; } return(0); } /////////////////////////////////////////////////////////////////////////////// // FUNCTION: OnInitDialog(HWND hDlg, HWND hWnd, LPARAM lParam) // // PARAMETERS: hDlg - // hWnd - // lParam - // // PURPOSE: WM_INITDIALOG message handler /////////////////////////////////////////////////////////////////////////////// BOOL OnInitDialog(HWND hDlg, HWND hWnd, LPARAM lParam) { bProcess = TRUE; // Just in case Advanced is launched as the startup page! if( !lpDIInterface ) { if( FAILED(DirectInputCreate(ghInstance, DIRECTINPUT_VERSION, &lpDIInterface, NULL)) ) { #ifdef _DEBUG OutputDebugString(TEXT("GCDEF.DLL: DirectInputCreate() failed\n")); #endif Error((short)IDS_INTERNAL_ERROR, (short)IDS_NO_DIJOYCONFIG); return(FALSE); } if( !pDIJoyConfig ) { if( FAILED(lpDIInterface->QueryInterface(IID_IDirectInputJoyConfig, (LPVOID*)&pDIJoyConfig)) ) { #ifdef _DEBUG OutputDebugString (TEXT("JOY.CPL: CoCreateInstance Failed... Closing CPL!\n")); #endif Error((short)IDS_INTERNAL_ERROR, (short)IDS_NO_DIJOYCONFIG); return(FALSE); } VERIFY(SUCCEEDED(pDIJoyConfig->SetCooperativeLevel(hDlg, DISCL_EXCLUSIVE | DISCL_BACKGROUND))); // Enumerate all the Types! VERIFY(SUCCEEDED(pDIJoyConfig->EnumTypes((LPDIJOYTYPECALLBACK)DIEnumJoyTypeProc, NULL))); // If you're here, you came in via the CMD line arg and you need to enumerate for devices so... lpDIInterface->EnumDevices(DIDEVTYPE_JOYSTICK, (LPDIENUMDEVICESCALLBACK)DIEnumDevicesProc, (LPVOID)hDlg, DIEDFL_ALLDEVICES); } } // if we find an object, then enable the Change... button //HWND hChangeCtrl = GetDlgItem(hDlg, IDC_ADV_CHANGE); // Determine Privilege and disable Change accordingly! if( pDIJoyConfig->Acquire() == DIERR_INSUFFICIENTPRIVS ) { // Assign here because the Advanced sheet could be launched first // via command line args! nFlags |= USER_MODE; //PostEnableWindow(hChangeCtrl, FALSE); } #ifdef WINNT else { // Run the WDMJOY.INF file!!! RunWDMJOY(); } #endif // set the global dialog handle ghDlg = hDlg; // blj: TODO: Make advanced page update on JOYCONFIGCHANGED message! // JOY_CONFIGCHANGED_MSGSTRING defined in MMDDK.H //JoyCfgChangedMsg = RegisterWindowMessage(JOY_CONFIGCHANGED_MSGSTRING); // initialize our list control hAdvListCtrl = GetDlgItem(hDlg, IDC_ADV_LIST_DEVICE); #ifdef _UNICODE // Set the Attributes! Removed LVS_EX_ONECLICKACTIVATE per GSeirra | LVS_EX_INFOTIP ::SendMessage(hAdvListCtrl, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_HEADERDRAGDROP); #else ::SendMessage(hAdvListCtrl, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP); #endif RECT rc; GetClientRect(hAdvListCtrl, &rc); rc.left = (short)((rc.right-GetSystemMetrics(SM_CXVSCROLL))/5); // Set up the columns! #ifdef _UNICODE InsertColumn(hAdvListCtrl, DEVICE_ID, IDS_ADV_DEVICE_HEADING, (USHORT)(rc.left >> 1 )); InsertColumn(hAdvListCtrl, DEVICE_FRIENDLY, IDS_ADV_DEVICE_FRIENDLY, (USHORT)(rc.left + (rc.left>>1))); InsertColumn(hAdvListCtrl, DEVICE_TYPE, IDS_ADV_GAME_CONTROLLERS, (USHORT)(rc.left << 1 )); InsertColumn(hAdvListCtrl, DEVICE_PORT, IDS_ADV_DEVICE_PORT, (USHORT)(rc.left )); // Remove the Global Port Driver stuff!!! const USHORT nCtrlArray[] = {IDC_TEXT_PORTDRIVER, IDC_COMBO1, IDC_ADV_GRP2, IDC_TEXT_DRIVER}; BYTE nIndex = sizeof(nCtrlArray)/sizeof(short); while( DestroyWindow(GetDlgItem(hDlg, nCtrlArray[--nIndex])) ); #else rc.right = (rc.left << 1) + (rc.left >> 2); InsertColumn(hAdvListCtrl, DEVICE_ID, IDS_ADV_DEVICE_HEADING, (USHORT)(rc.left >> 1)); InsertColumn(hAdvListCtrl, DEVICE_FRIENDLY, IDS_ADV_DEVICE_FRIENDLY, (USHORT)rc.right); InsertColumn(hAdvListCtrl, DEVICE_TYPE, IDS_ADV_GAME_CONTROLLERS, (USHORT)rc.right); #endif lpstrNone = new TCHAR[STR_LEN_32]; ASSERT (lpstrNone); // everyone needs the "None" string so I've loaded it here! VERIFY(LoadString(ghInstance, IDS_NONE, lpstrNone, STR_LEN_32)); fpMainWndProc = (WNDPROC)SetWindowLongPtr(hAdvListCtrl, GWLP_WNDPROC, (LONG_PTR)SubClassProc); // Only center the dialog if this was the page that we started on! if( nStartPageCPL == 1 ) { HWND hParentWnd = GetParent(hDlg); GetWindowRect(hParentWnd, &rc); // Centre the Dialog! SetWindowPos(hParentWnd, NULL, (GetSystemMetrics(SM_CXSCREEN) - (rc.right-rc.left))>>1, (GetSystemMetrics(SM_CYSCREEN) - (rc.bottom-rc.top))>>1, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); // Do that move button thing! MoveOK(hParentWnd); // Set the Update flag... nFlags |= UPDATE_FOR_ADV; } // the user is requesting that the CPL be shown // and an extention associated with nID be Launched. if( nID < NUMJOYDEVS ) { LaunchExtention(hDlg); // Zero out so you don't do it twice! nID = 0; } // SetActive will use this flag to make sure that the ListCtrl is populated! nFlags |= UPDATE_FOR_ADV; return(TRUE); } /////////////////////////////////////////////////////////////////////////////// // FUNCTION: OnCommand(HWND hDlg, int id, HWND hWndCtl, UINT code) // // PARAMETERS: hDlg - // id - // hWndCtl - // code - // // PURPOSE: WM_COMMAND message handler /////////////////////////////////////////////////////////////////////////////// void OnCommand(HWND hDlg, int id, HWND hWndCtl, UINT code) { // Hit the "What's This..." switch( id ) { case IDC_RENAME: // Only SubClass when we need to! if( !(nFlags & USER_MODE) ) { HWND hParentWnd = GetParent(GetDlgItem(hDlg, IDC_ADV_LIST_DEVICE)); // this is required because the CPL can be launched via RUNDLL32 if( ::IsWindow(hParentWnd) ) hParentWnd = GetParent(hParentWnd); if( !fpPageWndProc ) fpPageWndProc = (WNDPROC)SetWindowLongPtr(hParentWnd, GWLP_WNDPROC, (LONG_PTR)KeySubClassProc); nFlags |= UPDATE_INPROCESS; // Find the column as it Could have been moved! LPTSTR szName = new (TCHAR[STR_LEN_32]); ASSERT (szName); // First, Load the string of the column we are looking to find! if( LoadString(ghInstance, IDS_ADV_DEVICE_FRIENDLY, szName, STR_LEN_32) ) { // Now, traverse the columns to find the one with the title that matches szName! HWND hHeader = GetDlgItem(hAdvListCtrl, 0); BYTE nColumns = (BYTE)::SendMessage(hHeader, HDM_GETITEMCOUNT, 0, 0L); HDITEM *phdItem = new (HDITEM); ASSERT (phdItem); ZeroMemory(phdItem, sizeof(HD_ITEM)); phdItem->pszText = new TCHAR[STR_LEN_32]; ASSERT (phdItem->pszText); phdItem->cchTextMax = STR_LEN_32; phdItem->mask = HDI_TEXT | HDI_ORDER; do { ::SendMessage(hHeader, HDM_GETITEM, (WPARAM)(int)--nColumns, (LPARAM)(LPHDITEM)phdItem); if( _tcscmp(phdItem->pszText, szName) == 0 ) { nColumns = (BYTE)phdItem->iOrder; break; } } while( nColumns ); if( phdItem->pszText ) delete[] (phdItem->pszText); if( phdItem ) delete (phdItem); EditSubLabel( (BYTE)iAdvItem, nColumns ); } if( szName ) delete[] (szName); } break; case IDS_WHATSTHIS: { // point to help file LPTSTR pszHelpFileName = new TCHAR[STR_LEN_32]; ASSERT (pszHelpFileName); if( LoadString(ghInstance, IDS_HELPFILENAME, pszHelpFileName, STR_LEN_32) ) WinHelp((HWND)hAdvListCtrl, pszHelpFileName, HELP_WM_HELP, (ULONG_PTR)gaHelpIDs); #ifdef _DEBUG else OutputDebugString(TEXT("JOY.CPL: Advanced.cpp: OnCommand: LoadString Failed to find IDS_HELPFILENAME!\n")); #endif // _DEBUG if( pszHelpFileName ) delete[] (pszHelpFileName); } break; #ifndef _UNICODE // this is the handler for the Global Port Driver Combo box case IDC_COMBO1: if( code == CBN_SELCHANGE ) SetActiveGlobalDriver(); break; // handler for PollFlags entry in the registry for the Global Port Driver case IDC_POLLFLAGS: if( iGameportDriverItem == NO_ITEM ) break; if( SUCCEEDED(pDIJoyConfig->Acquire()) ) { HKEY hKey; VERIFY(SUCCEEDED(pDIJoyConfig->OpenTypeKey(pwszGameportDriverArray[iGameportDriverItem], KEY_ALL_ACCESS, &hKey))); // this entry is only valid if the user is running MSANALOG.VXD! DWORD nFlags = (IsDlgButtonChecked(hDlg, id)) ? 1 : 0; RegSetValueEx(hKey, POLL_FLAGS_REG_STR, 0, REG_BINARY, (PBYTE)&nFlags, sizeof(nFlags)); RegCloseKey(hKey); pDIJoyConfig->SendNotify(); pDIJoyConfig->Unacquire(); } break; #endif // _UNICODE // this is the handler for the Device list box case IDC_ADV_LIST_DEVICE: // Fall into Change on DBLCLK if( code != LBN_DBLCLK ) break; case IDC_ADV_CHANGE: if( nFlags & USER_MODE ) Error((short)IDS_USER_MODE_TITLE, (short)IDS_USER_MODE); else { LaunchChange(hDlg); } break; case IDC_ADV_USEOEMPAGE: if( !nAssigned ) { break; } if( IsWindowVisible(GetDlgItem(hDlg,IDC_ADV_USEOEMPAGE)) ) { // Boy are you going to pay the price for making that selection... LPDIJOYCONFIG_DX5 lpDIJoyConfig = new (DIJOYCONFIG_DX5); ASSERT (lpDIJoyConfig); ZeroMemory(lpDIJoyConfig, sizeof(DIJOYCONFIG_DX5)); lpDIJoyConfig->dwSize = sizeof (DIJOYCONFIG_DX5); // Get the index from the selected item (iAdvItem) BYTE n1 = (BYTE)GetItemData(hAdvListCtrl, (BYTE)iAdvItem); BYTE n = 0; do { if( pAssigned[n] && (n1 == pAssigned[n]->ID) ) break; n++; } while( n < NUMJOYDEVS ); // Find out the type name... if( SUCCEEDED(pDIJoyConfig->GetConfig(pAssigned[n]->ID, (LPDIJOYCONFIG)lpDIJoyConfig, DIJC_REGHWCONFIGTYPE)) ) { LPDIJOYTYPEINFO lpDIJoyTypeInfo = new (DIJOYTYPEINFO); ASSERT (lpDIJoyTypeInfo); ZeroMemory(lpDIJoyTypeInfo, sizeof(DIJOYTYPEINFO)); lpDIJoyTypeInfo->dwSize = sizeof(DIJOYTYPEINFO); // Get the TypeInfo you start with! if( SUCCEEDED(pDIJoyConfig->GetTypeInfo(lpDIJoyConfig->wszType, lpDIJoyTypeInfo, DITC_FLAGS1 | DITC_CLSIDCONFIG)) ) { DWORD dwFlags = GetItemData(hAdvListCtrl, (BYTE)iAdvItem); // If it's checked... you want the OEM supplied property sheet page! if( IsDlgButtonChecked(hDlg, IDC_ADV_USEOEMPAGE) ) { // Turn off the USE_DEFAULT flag dwFlags &= ~USE_DEFAULT; // Update the global pointer!!! pAssigned[n]->clsidPropSheet = lpDIJoyTypeInfo->clsidConfig; // Update the pointer being sent to the registry lpDIJoyTypeInfo->dwFlags1 &= ~JOYTYPE_DEFAULTPROPSHEET; } else { // Turn on the USE_DEFAULT flag dwFlags |= USE_DEFAULT; // Update the global list! pAssigned[n]->clsidPropSheet = CLSID_LegacyServer; // Update the pointer being sent to the registry lpDIJoyTypeInfo->dwFlags1 |= JOYTYPE_DEFAULTPROPSHEET; } if( SUCCEEDED(pDIJoyConfig->Acquire()) ) { // Update the registry VERIFY(SUCCEEDED(pDIJoyConfig->SetTypeInfo(lpDIJoyConfig->wszType, lpDIJoyTypeInfo, DITC_FLAGS1))); // Set the data in the list control! SetItemData(hAdvListCtrl, (BYTE)iAdvItem, dwFlags); } pDIJoyConfig->Unacquire(); } if( lpDIJoyTypeInfo ) delete (lpDIJoyTypeInfo); } if( lpDIJoyConfig ) delete (lpDIJoyConfig); } break; default: break; } } //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: OnNotify(HWND hDlg, WPARAM idFrom, NMHDR* pnmhdr) // // PARAMETERS: hDlg - // idFrom - ID of control sending WM_NOTIFY message // pnmhdr - // // PURPOSE: WM_NOTIFY message handler //////////////////////////////////////////////////////////////////////////////// BOOL OnNotify(HWND hDlg, WPARAM idFrom, LPNMHDR pnmhdr) { switch( pnmhdr->code ) { case PSN_QUERYCANCEL: if( nFlags & UPDATE_INPROCESS ) { nFlags &= ~UPDATE_INPROCESS; SetFocus(hAdvListCtrl); } break; /* case LVN_GETINFOTIP: { LPLVHITTESTINFO lpHit = new (LVHITTESTINFO); ASSERT (lpHit); BOOL bRet = FALSE; POINT pt; GetCursorPos(&pt); ScreenToClient(hAdvListCtrl, &pt); lpHit->pt = pt; lpHit->flags = lpHit->iItem = lpHit->iSubItem = 0; ::SendMessage(hAdvListCtrl, LVM_SUBITEMHITTEST, 0, (LPARAM)(LPLVHITTESTINFO)lpHit); if (lpHit->flags & LVHT_ONITEMLABEL) { // Determine the text length of the column text LPTSTR lpStr = new (TCHAR[MAX_STR_LEN+1]); ASSERT (lpStr); GetItemText(hAdvListCtrl, lpHit->iItem, lpHit->iSubItem, lpStr, MAX_STR_LEN); // Determine if the latter will fit inside the former... SIZE size; HDC hDC = GetDC(hAdvListCtrl); GetTextExtentPoint(hDC, lpStr, lstrlen(lpStr), &size); ReleaseDC(hAdvListCtrl, hDC); // Determine how wide the column is! short nWidth = (short)::SendMessage(hAdvListCtrl, LVM_GETCOLUMNWIDTH, lpHit->iSubItem, 0); bRet = (BOOL)(size.cx > nWidth); if (bRet) // if not, copy the text into lpHit->pszText _tcscpy(((LPNMLVGETINFOTIP)pnmhdr)->pszText, lpStr); if (lpStr) delete[] (lpStr); } if (lpHit) delete (lpHit); return bRet; } */ case LVN_BEGINLABELEDIT: if( !(GetItemData(hAdvListCtrl, (BYTE)iAdvItem) & ID_NONE) ) OnCommand(hDlg, IDC_RENAME, 0, 0); SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 1); break; case LVN_ENDLABELEDIT: if( !(nFlags & UPDATE_INPROCESS) ) return(FALSE); if( !bProcess ) return(FALSE); nFlags &= ~UPDATE_INPROCESS; if( fpPageWndProc ) { HWND hParentWnd = GetParent(hDlg); // this is required because the CPL can be launched via RUNDLL32 if( ::IsWindow(hParentWnd) ) hParentWnd = GetParent(hParentWnd); // Reset the subclass proc // SetWindowLongPtr(hParentWnd, GWLP_WNDPROC, (LONG_PTR)fpPageWndProc); } // Make sure the name is usable! if( _tcschr(((NMLVDISPINFO *)pnmhdr)->item.pszText, TEXT('\\')) ) { Error((short)IDS_INVALID_NAME_TITLE, (short)IDS_INVALID_NAME); } else { nFlags |= UPDATE_ALL; LPDIPROPSTRING pDIPropString = new (DIPROPSTRING); ASSERT (pDIPropString); ZeroMemory(pDIPropString, sizeof(DIPROPSTRING)); pDIPropString->diph.dwSize = sizeof(DIPROPSTRING); pDIPropString->diph.dwHeaderSize = sizeof(DIPROPHEADER); pDIPropString->diph.dwHow = DIPH_DEVICE; #ifdef _UNICODE wcscpy(pDIPropString->wsz, ((NMLVDISPINFO *)pnmhdr)->item.pszText); #else USES_CONVERSION; wcscpy(pDIPropString->wsz, A2W(((NMLVDISPINFO *)pnmhdr)->item.pszText)); #endif // Search nAssigned for the ID... BYTE n = nAssigned; do { if( pAssigned[--n]->ID == ((NMLVDISPINFO *)pnmhdr)->item.iItem ) break; } while( n ); if( SUCCEEDED(pAssigned[n]->fnDeviceInterface->SetProperty(DIPROP_INSTANCENAME, &pDIPropString->diph)) ) { SetItemText(hAdvListCtrl, (BYTE)((NMLVDISPINFO *)pnmhdr)->item.iItem, 1, ((NMLVDISPINFO *)pnmhdr)->item.pszText); } else { Error((short)IDS_NO_RENAME_TITLE, (short)IDS_NO_RENAME); } if( pDIPropString ) delete (pDIPropString); } break; #if 0 case LVN_COLUMNCLICK: switch( ((NM_LISTVIEW*)pnmhdr)->iSubItem ) { case DEVICE_ID: { static BOOL bIDDirection = TRUE; ::SendMessage(hAdvListCtrl, LVM_SORTITEMS, (WPARAM)(LPARAM)(bIDDirection =! bIDDirection), (LPARAM)(PFNLVCOMPARE)CompareIDItems); } break; default: { BOOL bDirection; CListCtrl *pCtrl = new (CListCtrl); ASSERT(pCtrl); pCtrl->Attach(hAdvListCtrl); switch( ((NM_LISTVIEW*)pnmhdr)->iSubItem ) { case DEVICE_FRIENDLY: { static BOOL bFriendlyDirection = FALSE; bDirection = (bFriendlyDirection =! bFriendlyDirection); } break; case DEVICE_TYPE: { static BOOL bTypeDirection = FALSE; bDirection = (bTypeDirection =! bTypeDirection); } break; case DEVICE_PORT: { static BOOL bPortDirection = FALSE; bDirection = (bPortDirection =! bPortDirection); } break; } SortTextItems(pCtrl, (short)((NM_LISTVIEW*)pnmhdr)->iSubItem, bDirection, 0, 15); pCtrl->Detach(); if( pCtrl ) delete (pCtrl); } break; } if( nAssigned ) { iAdvItem = (short)::SendMessage(hAdvListCtrl, LVM_GETNEXTITEM, (WPARAM)(int)-1, MAKELPARAM(LVNI_SELECTED, 0)); ::PostMessage(hAdvListCtrl, LVM_ENSUREVISIBLE, iAdvItem, TRUE); if( !(nFlags & USER_MODE) ) PostDlgItemEnableWindow(hDlg, IDC_ADV_CHANGE, (GetItemData(hAdvListCtrl, (BYTE)iAdvItem) & ID_NONE) ? FALSE : TRUE); SetListCtrlItemFocus(hAdvListCtrl, (BYTE)iAdvItem); } break; #endif case PSN_KILLACTIVE: if( nFlags & UPDATE_INPROCESS ) SetFocus(hAdvListCtrl); #ifdef _UNICODE if( hAdvNotifyDevNode ) UnregisterDeviceNotification(hAdvNotifyDevNode); #endif break; case NM_DBLCLK: switch( idFrom ) { case IDC_ADV_LIST_DEVICE: if( !(GetItemData(hAdvListCtrl, (BYTE)iAdvItem) & ID_NONE) ) LaunchChange(hDlg); break; } break; case PSN_SETACTIVE: #ifdef _UNICODE RegisterForDevChange(hDlg, &hAdvNotifyDevNode); #endif if( nFlags & UPDATE_FOR_ADV ) { if( !AdvUpdateListCtrl() ) { #ifdef _DEBUG OutputDebugString(TEXT("JOY.CPL: OnNotify: Failed UpdateListCtrl!\n")); #endif } } if( nAssigned ) { iAdvItem = 0; // This will happen when the user comes in via the CMD line! if( iItem != NO_ITEM ) { // Find the ID of the device... the Brute Force Method! do { if( (pAssigned[iItem] != NULL) && ((BYTE)GetItemData(hAdvListCtrl, (BYTE)iAdvItem) == pAssigned[iItem]->ID) ) break; iAdvItem++; } while( iAdvItem < NUMJOYDEVS ); } if( iAdvItem == NUMJOYDEVS ) { iAdvItem = 0; } SetListCtrlItemFocus(hAdvListCtrl, (BYTE)iAdvItem); ::PostMessage(hAdvListCtrl, LVM_ENSUREVISIBLE, iAdvItem, FALSE ); } // No global port drivers in NT so... //if (nGameportDriver) #ifndef _UNICODE if( !(nFlags & ON_NT) ) PopulateGlobalPortDriverComboBox(); #endif // disable the Change button if iAdvItem points to a (none) selection if( !(nFlags & USER_MODE) ) PostDlgItemEnableWindow(hDlg, IDC_ADV_CHANGE, (nAssigned) ? ((iAdvItem & ID_NONE) ? FALSE : TRUE) : FALSE); break; case LVN_ITEMCHANGED: if( iAdvItem != (short)((NM_LISTVIEW*)pnmhdr)->iItem ) { iAdvItem = (short)((NM_LISTVIEW*)pnmhdr)->iItem; HWND hCtrl = GetDlgItem(hDlg, IDC_ADV_USEOEMPAGE); if( nAssigned ) { SetWindowPos( hCtrl, NULL, NULL, NULL, NULL, NULL, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | ((((NM_LISTVIEW*)pnmhdr)->lParam & SHOW_DEFAULT) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW) ); // Check the box appropriatly! if( ((NM_LISTVIEW*)pnmhdr)->lParam & SHOW_DEFAULT ) ::PostMessage(GetDlgItem(hDlg, IDC_ADV_USEOEMPAGE), BM_SETCHECK, (((NM_LISTVIEW*)pnmhdr)->lParam & USE_DEFAULT) ? BST_UNCHECKED : BST_CHECKED, 0); if( ((NM_LISTVIEW*)pnmhdr)->lParam ) PostEnableWindow(hCtrl, (BOOL)!(((NM_LISTVIEW*)pnmhdr)->lParam & ID_NONE)); if( !(nFlags & USER_MODE) ) PostDlgItemEnableWindow(hDlg, IDC_ADV_CHANGE, (BOOL)!(((NM_LISTVIEW*)pnmhdr)->lParam & ID_NONE)); } else SetWindowPos( hCtrl, NULL, NULL, NULL, NULL, NULL, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_HIDEWINDOW); } break; case LVN_KEYDOWN: switch( ((LV_KEYDOWN*)pnmhdr)->wVKey ) { case VK_DELETE: iAdvItem = (short)::SendMessage(hAdvListCtrl, LVM_GETNEXTITEM, (WPARAM)(int)-1, MAKELPARAM(LVNI_SELECTED, 0)); { BYTE nRet = (BYTE)GetItemData(hAdvListCtrl, (BYTE)iAdvItem); DeleteSelectedItem((PBYTE)&nRet); } // Missing break intentional! case VK_F5: Enumerate( hDlg ); AdvUpdateListCtrl(); SetListCtrlItemFocus(hAdvListCtrl, (BYTE)iAdvItem); ::PostMessage(hAdvListCtrl, LVM_ENSUREVISIBLE, iAdvItem, FALSE ); break; } break; } return(TRUE); } //////////////////////////////////////////////////////////////////////////////////////// // FUNCTION: OnDestroy ( HWND hWnd ) // // PARAMETERS: hWnd - Handle to window being destroyed // // PURPOSE: WM_DESTROY message handler //////////////////////////////////////////////////////////////////////////////////////// void OnDestroy(HWND hWnd) { ASSERT (hWnd); if( lpstrNone ) delete[] (lpstrNone); // Reset the subclass proc SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)fpMainWndProc); // release the DI JoyConfig interface pointer if( pDIJoyConfig ) { pDIJoyConfig->Release(); pDIJoyConfig = 0; } } //////////////////////////////////////////////////////////////////////////////////////// // FUNCTION: OnAdvHelp ( LPARAM lParam ) // // PARAMETERS: lParam - Pointer to HELPINFO struct // // PURPOSE: WM_HELP message handler //////////////////////////////////////////////////////////////////////////////////////// void OnAdvHelp(LPARAM lParam) { ASSERT (lParam); // point to help file LPTSTR pszHelpFileName = new TCHAR[STR_LEN_32]; ASSERT (pszHelpFileName); if( LoadString(ghInstance, IDS_HELPFILENAME, pszHelpFileName, STR_LEN_32) ) { if( ((LPHELPINFO)lParam)->iContextType == HELPINFO_WINDOW ) WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, pszHelpFileName, HELP_WM_HELP, (ULONG_PTR)gaHelpIDs); } #ifdef _DEBUG else OutputDebugString(TEXT("JOY.CPL: Advanced.cpp: OnAdvHelp: LoadString Failed to find IDS_HELPFILENAME!\n")); #endif // _DEBUG if( pszHelpFileName ) delete[] (pszHelpFileName); } //////////////////////////////////////////////////////////////////////////////////////// // FUNCTION: OnContextMenu ( WPARAM wParam ) // // PARAMETERS: wParam - HWND of window under the pointer // // PURPOSE: Handle WM_RBUTTONDOWN over all client windows // (except the list control... that's OnListviewContextMenu() job) //////////////////////////////////////////////////////////////////////////////////////// void OnContextMenu(WPARAM wParam, LPARAM lParam) { ASSERT (wParam); // If you are on the ListCtrl... if( (HWND)wParam == hAdvListCtrl ) { SetFocus(hAdvListCtrl); // Don't attempt if nothing selected if( iAdvItem != NO_ITEM ) OnListviewContextMenu(lParam); } else { // point to help file LPTSTR pszHelpFileName = new TCHAR[STR_LEN_32]; ASSERT (pszHelpFileName); if( LoadString(ghInstance, IDS_HELPFILENAME, pszHelpFileName, STR_LEN_32) ) WinHelp((HWND)wParam, pszHelpFileName, HELP_CONTEXTMENU, (ULONG_PTR)gaHelpIDs); #ifdef _DEBUG else OutputDebugString(TEXT("JOY.CPL: Advanced.cpp: OnContextMenu: LoadString Failed to find IDS_HELPFILENAME!\n")); #endif // _DEBUG if( pszHelpFileName ) delete[] (pszHelpFileName); } } /////////////////////////////////////////////////////////////////////////////// // FUNCTION: SetActiveGlobalDriver( void ) // // PURPOSE: Commit user selection to persistent storage. // // RETURN: TRUE if successfull, FALSE otherwise /////////////////////////////////////////////////////////////////////////////// #ifndef _UNICODE BOOL SetActiveGlobalDriver( void ) { // It's Perfectly valid to not have a Global Port Driver so... be prepared! short n = (short)SendDlgItemMessage(ghDlg, IDC_COMBO1, CB_GETCURSEL, 0, 0); if( n == CB_ERR ) return(FALSE); LPDIJOYUSERVALUES pDIJoyUserValues = new (DIJOYUSERVALUES); ASSERT (pDIJoyUserValues); ZeroMemory(pDIJoyUserValues, sizeof(DIJOYUSERVALUES)); pDIJoyUserValues->dwSize = sizeof(DIJOYUSERVALUES); HWND hCtrl = GetDlgItem(ghDlg, IDC_COMBO1); // Don't worry about this not being a TCHAR, this code will never be executed in NT! LPSTR pszDisplayName = new char[SendMessage(hCtrl, LB_GETTEXTLEN, (WPARAM)n, 0)+1]; ASSERT (pszDisplayName); SendMessage(hCtrl, CB_GETLBTEXT, n, (LPARAM)(LPCTSTR)pszDisplayName); hCtrl = GetDlgItem(ghDlg, IDC_POLLFLAGS); SetWindowPos( hCtrl, NULL, NULL, NULL, NULL, NULL, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_HIDEWINDOW ); // Fix #9815, Set wszGlobalDriver to if( _tcsncmp(pszDisplayName, lpstrNone, sizeof(lpstrNone)/sizeof(TCHAR)) == 0 ) { //wcscpy(pDIJoyUserValues->wszGlobalDriver, L""); if( SUCCEEDED(pDIJoyConfig->Acquire()) ) { if( FAILED(pDIJoyConfig->SetUserValues(pDIJoyUserValues, DIJU_GLOBALDRIVER)) ) { TRACE (TEXT("JOY.CPL: SetUserValues failed to set DIJU_GLOBALDRIVER!\n")); } } } else { LPDIJOYTYPEINFO lpdiJoyInfo = new DIJOYTYPEINFO; ASSERT (lpdiJoyInfo); ZeroMemory(lpdiJoyInfo, sizeof(DIJOYTYPEINFO)); lpdiJoyInfo->dwSize = sizeof(DIJOYTYPEINFO); USES_CONVERSION; short nIndex = 0; // traverse the list of Global Port Drivers 'till you find the matching display name // this also disallows the user from doing something ugly when they only have "standard gameport" while( pwszGameportDriverArray[nIndex] ) { // populate the Type Info if( SUCCEEDED(pDIJoyConfig->GetTypeInfo(pwszGameportDriverArray[nIndex], lpdiJoyInfo, DITC_DISPLAYNAME | DITC_CALLOUT)) ) { if( _wcsicmp(lpdiJoyInfo->wszDisplayName, A2W(pszDisplayName)) == 0 ) { wcscpy(pDIJoyUserValues->wszGlobalDriver, lpdiJoyInfo->wszCallout ); if( SUCCEEDED(pDIJoyConfig->Acquire()) ) { if( FAILED(pDIJoyConfig->SetUserValues(pDIJoyUserValues, DIJU_GLOBALDRIVER)) ) { TRACE (TEXT("JOY.CPL: SetUserValues failed to set DIJU_GLOBALDRIVER!\n")); } // check to see if you need to display the poll flags check box! if( _wcsicmp(pDIJoyUserValues->wszGlobalDriver, lpMSANALOG_VXD) == 0 ) { SetWindowPos( hCtrl, NULL, NULL, NULL, NULL, NULL, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW ); // Get the state from the registry and update the check mark HKEY hKey; if( FAILED(pDIJoyConfig->OpenTypeKey(pwszGameportDriverArray[nIndex], KEY_ALL_ACCESS, &hKey)) ) { TRACE (TEXT("JOY.CPL: OpenTypeKey failed to open key %s!\n"), pwszGameportDriverArray[nIndex]); } DWORD dwFlag; ULONG ulType = REG_BINARY; ULONG ulSize = sizeof(dwFlag); // this will happen if there is no entry for POLL_FLAGS_REG_STR if( ERROR_SUCCESS != RegQueryValueEx(hKey, POLL_FLAGS_REG_STR, NULL, &ulType, (PBYTE)&dwFlag, &ulSize) ) dwFlag = 0; ::PostMessage(GetDlgItem(ghDlg, IDC_POLLFLAGS), BM_SETCHECK, (dwFlag) ? BST_CHECKED : BST_UNCHECKED, 0); RegCloseKey(hKey); } } break; } } nIndex++; } // delete the DIJOYTYPEINFO variable if( lpdiJoyInfo ) delete (lpdiJoyInfo); } pDIJoyConfig->SendNotify(); pDIJoyConfig->Unacquire(); if( pszDisplayName ) delete[] (pszDisplayName); if( pDIJoyUserValues ) delete pDIJoyUserValues; return(TRUE); } #endif // _UNICODE /////////////////////////////////////////////////////////////////////////////// // FUNCTION: OnListviewContextMenu( void ) // // PURPOSE: Handle Context menu in Listview // // RETURN: TRUE if successfull, FALSE otherwise /////////////////////////////////////////////////////////////////////////////// static void OnListviewContextMenu( LPARAM lParam ) { HMENU hPopupMenu = CreatePopupMenu(); ASSERT (hPopupMenu); // unlike life, bRet defaults to bliss BOOL bRet = TRUE; LPTSTR pszText = new TCHAR[STR_LEN_32]; ASSERT (pszText); // Don't display Rename/Change if on (none) entry if( !(GetItemData(hAdvListCtrl, (BYTE)iAdvItem) & ID_NONE) ) { if( !(nFlags & USER_MODE) ) { // add the "Change..." string ::SendDlgItemMessage(GetParent(hAdvListCtrl), IDC_ADV_CHANGE, WM_GETTEXT, (WPARAM)STR_LEN_32, (LPARAM)(LPCTSTR)pszText); bRet = AppendMenu(hPopupMenu, MF_ENABLED, IDC_ADV_CHANGE, pszText); #ifdef _DEBUG if( !bRet ) TRACE(TEXT("JOY.CPL: OnListviewCOntextMenu: AppendMenu Failed to insert %s\n"), pszText); #endif //_DEBUG // Add the Rename text VERIFY(LoadString(ghInstance, IDS_RENAME, pszText, STR_LEN_32)); bRet = AppendMenu(hPopupMenu, MF_ENABLED, IDC_RENAME, pszText); #ifdef _DEBUG if( !bRet ) TRACE(TEXT("JOY.CPL: OnListviewCOntextMenu: AppendMenu Failed to insert %s\n"), pszText); #endif //_DEBUG // Add the SEPERATOR and "What's this?" //PREFIX #WI279965. False positive. //MSDN: if uFlags==MF_SEPARATOR, LPCTSTR lpNewItem is ignored. bRet = AppendMenu(hPopupMenu, MF_SEPARATOR, 0, 0); #ifdef _DEBUG if( !bRet ) TRACE(TEXT("JOY.CPL: OnListviewCOntextMenu: AppendMenu Failed to insert SEPERATOR!\n")); #endif //_DEBUG } } VERIFY(LoadString(ghInstance, IDS_WHATSTHIS, pszText, STR_LEN_32)); bRet = AppendMenu(hPopupMenu, MF_ENABLED, IDS_WHATSTHIS, pszText); #ifdef _DEBUG if( !bRet ) TRACE(TEXT("JOY.CPL: OnListviewCOntextMenu: AppendMenu Failed to insert %s\n"), pszText); #endif //_DEBUG if( pszText ) delete[] (pszText); POINT pt; // lParam is -1 if we got here via Shift+F10 if( lParam > 0 ) { pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam); } else { // Centre the popup on the selected item! // This get's a good X pos, but the y is the start of the control! ::SendMessage(hAdvListCtrl, LVM_GETITEMPOSITION, iAdvItem, (LPARAM)&pt); RECT rc; ::GetClientRect(hAdvListCtrl, &rc); pt.x = rc.right>>1; ClientToScreen(hAdvListCtrl, &pt); } bRet = TrackPopupMenu (hPopupMenu, TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_RIGHTBUTTON, pt.x, pt.y, 0, ghDlg, NULL); #ifdef _DEBUG if( !bRet ) TRACE (TEXT("JOY.CPL: OnListviewContextMenu: TrackPopupMenu Failed!\n")); #endif //_DEBUG if(hPopupMenu) DestroyMenu (hPopupMenu); // PREFIX 45089 } /////////////////////////////////////////////////////////////////////////////// // FUNCTION: UpdateListCtrl( void ) // // PURPOSE: Refreshes enumerated device list // // RETURN: TRUE if successfull, FALSE otherwise /////////////////////////////////////////////////////////////////////////////// static BOOL AdvUpdateListCtrl() { // Turn Redraw off here else it will flicker! ::SendMessage(hAdvListCtrl, WM_SETREDRAW, (WPARAM)FALSE, 0); // Out with the old... ::SendMessage(hAdvListCtrl, LVM_DELETEALLITEMS, 0, 0); // This buffer is so large because it is used to hold IDS_GEN_STATUS_UNKNOWN TCHAR sz1[16]; // find and assign ID's BYTE n = NUMJOYDEVS; BYTE nIndex; SendMessage(hAdvListCtrl, LVM_SETITEMCOUNT, (WPARAM)(int)NUMJOYDEVS, (LPARAM)LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL); // Set everything to NONE to start! do { itoa((BYTE)n--, (LPTSTR)&sz1); // Insert the ID // Set the device ID and ID_NONE in Extended info... nIndex = InsertItem( hAdvListCtrl, sz1, n); // Populate the columns with "(none)" SetItemText(hAdvListCtrl, nIndex, DEVICE_FRIENDLY, lpstrNone); SetItemText(hAdvListCtrl, nIndex, DEVICE_TYPE, lpstrNone); #ifdef _UNICODE SetItemText(hAdvListCtrl, nIndex, DEVICE_PORT, lpstrNone); #endif } while( n ); if( nAssigned ) { // insert the assigned ones! n = nAssigned; DIPROPSTRING *pDIPropStr = new (DIPROPSTRING); ASSERT (pDIPropStr); ZeroMemory(pDIPropStr, sizeof(DIPROPSTRING)); pDIPropStr->diph.dwSize = sizeof(DIPROPSTRING); pDIPropStr->diph.dwHeaderSize = sizeof(DIPROPHEADER); pDIPropStr->diph.dwHow = DIPH_DEVICE; #ifndef _UNICODE USES_CONVERSION; #endif // The low half will be populated by the ID, the upper by bit flags! DWORD dwData; do { // Set the Product Column! if( SUCCEEDED(pAssigned[--n]->fnDeviceInterface->GetProperty(DIPROP_PRODUCTNAME, &pDIPropStr->diph)) ) { #ifdef _UNICODE SetItemText(hAdvListCtrl, pAssigned[n]->ID, DEVICE_TYPE, (LPTSTR)pDIPropStr->wsz); #else SetItemText(hAdvListCtrl, pAssigned[n]->ID, DEVICE_TYPE, (LPTSTR)W2A(pDIPropStr->wsz)); #endif } // Set the Friendly Name! if( SUCCEEDED(pAssigned[n]->fnDeviceInterface->GetProperty(DIPROP_INSTANCENAME, &pDIPropStr->diph)) ) { #ifdef _UNICODE SetItemText(hAdvListCtrl, pAssigned[n]->ID, DEVICE_FRIENDLY, (LPTSTR)pDIPropStr->wsz); #else SetItemText(hAdvListCtrl, pAssigned[n]->ID, DEVICE_FRIENDLY, (LPTSTR)W2A(pDIPropStr->wsz)); #endif } #ifdef _UNICODE // Set the Game Port Column! if( SUCCEEDED(pAssigned[n]->fnDeviceInterface->GetProperty(DIPROP_GETPORTDISPLAYNAME, &pDIPropStr->diph)) ) { SetItemText(hAdvListCtrl, pAssigned[n]->ID, DEVICE_PORT, (LPTSTR)pDIPropStr->wsz); } else { VERIFY(LoadString(ghInstance, IDS_GEN_STATUS_UNKNOWN, sz1, sizeof(sz1)/sizeof(TCHAR))); SetItemText(hAdvListCtrl, pAssigned[n]->ID, DEVICE_PORT, (LPTSTR)sz1); } #endif // _UNICODE // Set the ID in the data... // This is necessary for Sorting! dwData = pAssigned[n]->ID; //if( pAssigned[n]->clsidPropSheet != CLSID_LegacyServer ) if( pAssigned[n]->fHasOemSheet ) { LPDIJOYCONFIG_DX5 lpDIJoyConfig = new (DIJOYCONFIG_DX5); ASSERT (lpDIJoyConfig); ZeroMemory(lpDIJoyConfig, sizeof(DIJOYCONFIG_DX5)); lpDIJoyConfig->dwSize = sizeof (DIJOYCONFIG_DX5); // Set the DefaultPropertySheet flag if( SUCCEEDED(pDIJoyConfig->GetConfig(pAssigned[n]->ID, (LPDIJOYCONFIG)lpDIJoyConfig, DIJC_REGHWCONFIGTYPE)) ) { LPDIJOYTYPEINFO lpDIJoyTypeInfo = new (DIJOYTYPEINFO); ASSERT (lpDIJoyTypeInfo); ZeroMemory(lpDIJoyTypeInfo, sizeof(DIJOYTYPEINFO)); lpDIJoyTypeInfo->dwSize = sizeof (DIJOYTYPEINFO); if( SUCCEEDED(pDIJoyConfig->GetTypeInfo(lpDIJoyConfig->wszType, lpDIJoyTypeInfo, DITC_FLAGS1 )) ) { if( lpDIJoyTypeInfo->dwFlags1 & JOYTYPE_DEFAULTPROPSHEET ) { // Set the USE_DEFAULT mask! dwData |= USE_DEFAULT; // Update the global list! pAssigned[n]->clsidPropSheet = CLSID_LegacyServer; } } if( lpDIJoyTypeInfo ) delete (lpDIJoyTypeInfo); } dwData |= SHOW_DEFAULT; if( lpDIJoyConfig ) delete (lpDIJoyConfig); } // Set the Item Data to the ID! SetItemData(hAdvListCtrl, pAssigned[n]->ID, dwData); } while( n ); // clean up, clean up... everybody do your share! if( pDIPropStr ) delete (pDIPropStr); } // turn the flag off! if( nFlags & UPDATE_FOR_ADV ) nFlags &= ~UPDATE_FOR_ADV; // Turn the redraw flag back on! ::SendMessage (hAdvListCtrl, WM_SETREDRAW, (WPARAM)TRUE, 0); InvalidateRect(hAdvListCtrl, NULL, TRUE); return(TRUE); } //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: ChangeDialogProc(HWND hDlg, ULONG uMsg, WPARAM wParam, LPARAM lParam) // // PARAMETERS: HWND hDlg - // ULONG uMsg - // WPARAM wParam - // LPARAM lParam - // // PURPOSE: //////////////////////////////////////////////////////////////////////////////// BOOL WINAPI ChangeDialogProc(HWND hDlg, ULONG uMsg, WPARAM wParam, LPARAM lParam) { switch( uMsg ) { case WM_LBUTTONDOWN: // Click Drag service for PropSheets! PostMessage(hDlg, WM_NCLBUTTONDOWN, (WPARAM)HTCAPTION, lParam); break; case WM_HELP: OnHelp(lParam); return(1); case WM_CONTEXTMENU: OnContextMenu(wParam, lParam); return(TRUE); case WM_INITDIALOG: { HICON hIcon = (HICON)LoadImage(ghInstance, MAKEINTRESOURCE(IDI_CPANEL), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); ASSERT (hIcon); if( hIcon ) ::PostMessage(hDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon); HWND hCtrl = GetDlgItem(hDlg, IDC_CHANGE_LIST); ASSERT (hCtrl); UpdateChangeListCtrl( hCtrl ); BYTE nCounter = nAssigned; while( nCounter-- ) { if( (BYTE)::SendMessage(hCtrl, LB_GETITEMDATA, (WPARAM)nCounter, 0) == nOldID ) break; } // Set the list box selections! ::PostMessage(hCtrl, LB_SETCURSEL, (WPARAM)nCounter, 0); // Done with the ListCtrl, now... on to the ComboBox if( nFlags & ON_NT ) { if( !PopulatePortList(hDlg) ) { #ifdef _DEBUG OutputDebugString(TEXT("JOY.CPL: ADVANCED.CPP: PopulatePortList failed!\n")); #endif } } // Set up the Spin Control! HWND hSpinCtrl = GetDlgItem(hDlg, IDC_SPIN); ::PostMessage(hSpinCtrl, UDM_SETRANGE, 0, MAKELPARAM(NUMJOYDEVS, 1)); ::PostMessage(hSpinCtrl, UDM_SETBASE, 10, 0L); ::PostMessage(hSpinCtrl, UDM_SETPOS, 0, MAKELPARAM(nOldID+1, 0)); } return(FALSE); case WM_COMMAND: { switch( LOWORD(wParam) ) { /* I'll leave this commented out, as it's likely they'll want double clicking back once I take it out case IDC_CHANGE_LIST: // Fall into Change on DBLCLK if (HIWORD(wParam) != LBN_DBLCLK) break; */ case IDOK: { HWND hCtrl = GetDlgItem(hDlg, IDC_CHANGE_LIST); ASSERT (hCtrl); char nSelectedItem = (char)(SendMessage(hCtrl, LB_GETITEMDATA, SendMessage(hCtrl, LB_GETCURSEL, 0, 0), 0)); TCHAR tsz[4]; hCtrl = GetDlgItem(hDlg, IDC_SPINBUDDY); tsz[0] = 4; SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM)(LPCSTR)&tsz); // The '-1' is to account for the 1 based list // and the 0 based id's! char nSelectedID = (char)atoi((LPCTSTR)&tsz)-1; pDIJoyConfig->Acquire(); // first check to see if the user has selected NONE! if( nSelectedItem == -2 ) { // User has selected NONE! VERIFY (SUCCEEDED(pDIJoyConfig->DeleteConfig(nSelectedID))); } else { // see if the selected item and the ID match! // if so... get out of here! if( nSelectedID == nSelectedItem ) { #ifdef _DEBUG OutputDebugString(TEXT("JOY.CPL: ADVANCED.CPP: OnChangeCommand: IDOK: Device already at selected ID!\n")); #endif } else { SwapIDs(nSelectedID, nSelectedItem); // SetListCtrlItemFocus(hAdvListCtrl, (BYTE)nSelectedID); } } pDIJoyConfig->Unacquire(); } // missing break intentional! case IDCANCEL: EndDialog(hDlg, LOWORD(wParam)); break; } } return(1); case WM_DESTROY: DestroyIcon((HICON)SendMessage(hDlg, WM_GETICON, (WPARAM)ICON_SMALL, 0)); return(TRUE); } return(FALSE); } /////////////////////////////////////////////////////////////////////////////// // FUNCTION: PopulateGlobalPortDriverComboBox( void ) // // PARAMETERS: // // PURPOSE: /////////////////////////////////////////////////////////////////////////////// #ifndef UNICODE void PopulateGlobalPortDriverComboBox( void ) { HWND hCtrl = GetDlgItem(ghDlg, IDC_COMBO1); // make sure the combo is clear before we start populating it! SendMessage(hCtrl, CB_RESETCONTENT, 0, 0); // Type info LPDIJOYTYPEINFO_DX5 lpdiJoyInfo = new (DIJOYTYPEINFO_DX5); ASSERT (lpdiJoyInfo); ZeroMemory(lpdiJoyInfo, sizeof(DIJOYTYPEINFO_DX5)); lpdiJoyInfo->dwSize = sizeof(DIJOYTYPEINFO_DX5); BYTE nIndex = nGameportDriver; USES_CONVERSION; // Populate the Combobox while( nIndex-- ) { // populate the Type Info and place the Index in the extra memory! if( SUCCEEDED(pDIJoyConfig->GetTypeInfo(pwszGameportDriverArray[nIndex], (LPDIJOYTYPEINFO)lpdiJoyInfo, DITC_DISPLAYNAME)) ) ::SendMessage(hCtrl, CB_SETITEMDATA, ::SendMessage(hCtrl, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)W2A(lpdiJoyInfo->wszDisplayName)), nIndex); #ifdef _DEBUG else OutputDebugString (TEXT("JOY.CPL: ADVANCED.CPP: PopulateGlobalPortDriverComboBox: GetTypeInfo failed!\n")); #endif } // Display the Current selected GlobalPortDriver or None! LPDIJOYUSERVALUES pDIJoyUserValues = new DIJOYUSERVALUES; ASSERT (pDIJoyUserValues); ZeroMemory(pDIJoyUserValues, sizeof(DIJOYUSERVALUES)); pDIJoyUserValues->dwSize = sizeof(DIJOYUSERVALUES); VERIFY (SUCCEEDED(pDIJoyConfig->GetUserValues(pDIJoyUserValues, DIJU_GLOBALDRIVER))); // Fix #9815, If the user has No Global port driver label as NONE! if( !(*pDIJoyUserValues->wszGlobalDriver && pDIJoyUserValues->wszGlobalDriver) ) { iGameportDriverItem = NO_ITEM; PostMessage(hCtrl, CB_SETCURSEL, (WPARAM)SendMessage(hCtrl, CB_FINDSTRING, (WPARAM)-1, (LPARAM)lpstrNone), (LPARAM)0); } else { ZeroMemory(lpdiJoyInfo, sizeof(DIJOYTYPEINFO_DX5)); lpdiJoyInfo->dwSize = sizeof(DIJOYTYPEINFO_DX5); nIndex = 0; // get type info 'till you find the one you want and place it's callout while( pwszGameportDriverArray[nIndex] ) { VERIFY(SUCCEEDED(pDIJoyConfig->GetTypeInfo(pwszGameportDriverArray[nIndex], (LPDIJOYTYPEINFO)lpdiJoyInfo, DITC_CALLOUT | DITC_DISPLAYNAME))); if( _wcsicmp(lpdiJoyInfo->wszCallout, pDIJoyUserValues->wszGlobalDriver) == 0 ) { ::PostMessage(hCtrl, CB_SETCURSEL, (WPARAM)::SendMessage(hCtrl, CB_FINDSTRING, (WPARAM)-1, (LPARAM)W2A(lpdiJoyInfo->wszDisplayName)), (LPARAM)0); iGameportDriverItem = nIndex; // enable the PollFlags checkbox! if( _wcsicmp(pDIJoyUserValues->wszGlobalDriver, lpMSANALOG_VXD) == 0 ) { SetWindowPos( GetDlgItem( ghDlg, IDC_POLLFLAGS), NULL, NULL, NULL, NULL, NULL, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW ); VERIFY(SUCCEEDED(pDIJoyConfig->Acquire())); // Get the state from the registry and update the check mark HKEY hKey; DWORD dwFlag; if( SUCCEEDED(pDIJoyConfig->OpenTypeKey(pwszGameportDriverArray[nIndex], KEY_ALL_ACCESS, &hKey)) ) { ULONG ulType = REG_BINARY; ULONG ulSize = sizeof(dwFlag); // this will happen if there is no entry for POLL_FLAGS_REG_STR if( ERROR_SUCCESS != RegQueryValueEx(hKey, POLL_FLAGS_REG_STR, NULL, &ulType, (PBYTE)&dwFlag, &ulSize) ) dwFlag = 0; RegCloseKey(hKey); } pDIJoyConfig->Unacquire(); ::PostMessage(GetDlgItem(ghDlg, IDC_POLLFLAGS), BM_SETCHECK, (dwFlag) ? BST_CHECKED : BST_UNCHECKED, 0); } break; } nIndex++; } } // delete the DIJOYUSERVALUES variable if( pDIJoyUserValues ) delete pDIJoyUserValues; // clean up, clean up... everybody do your share! if( lpdiJoyInfo ) delete (lpdiJoyInfo); // VERIFY(SendMessage(hCtrl, CB_ADDSTRING, 0, (LPARAM)(LPCSTR)lpstrNone) != CB_ERR); } #endif /////////////////////////////////////////////////////////////////////////////// // FUNCTION: SubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) // // PARAMETERS: HWND hWnd - // UINT uMsg - // WPARAM wParam - // LPARAM lParam - // // PURPOSE: SubClass Procedure for Setting the ID to NONE on VK_DELETE on the Advanced Page /////////////////////////////////////////////////////////////////////////////// BOOL WINAPI KeySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch( uMsg ) { case WM_COMMAND: switch( LOWORD(wParam) ) { case IDCANCEL: case IDOK: if( nFlags & UPDATE_INPROCESS ) { bProcess = (LOWORD(wParam) == IDOK) ? TRUE : FALSE; SetFocus(hAdvListCtrl); nFlags &= ~UPDATE_INPROCESS; return(FALSE); } break; } break; } return(BOOL)CallWindowProc(fpPageWndProc, hWnd, uMsg, wParam, lParam); } /////////////////////////////////////////////////////////////////////////////// // FUNCTION: SubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) // // PARAMETERS: HWND hWnd - // UINT uMsg - // WPARAM wParam - // LPARAM lParam - // // PURPOSE: SubClass Procedure for Setting the ID to NONE on VK_DELETE on the Advanced Page /////////////////////////////////////////////////////////////////////////////// BOOL WINAPI SubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch( uMsg ) { case WM_PARENTNOTIFY: if( LOWORD(wParam) == WM_LBUTTONDOWN ) { if( nFlags & UPDATE_INPROCESS ) SetFocus(hAdvListCtrl); } break; case WM_SYSCOMMAND: if( wParam & SC_VSCROLL ) { if( nFlags & UPDATE_INPROCESS ) SetFocus(hAdvListCtrl); } break; } return(BOOL)CallWindowProc(fpMainWndProc, hWnd, uMsg, wParam, lParam); } /////////////////////////////////////////////////////////////////////////////// // FUNCTION: UpdateChangeListCtrl ( HWND hCtrl ) // // PARAMETERS: HWND hCtrl - Handle to Change list box // // PURPOSE: Update the Change List box /////////////////////////////////////////////////////////////////////////////// static BOOL UpdateChangeListCtrl ( HWND hCtrl ) { #ifndef _UNICODE USES_CONVERSION; #endif DIPROPSTRING *pDIPropStr = new (DIPROPSTRING); ASSERT (pDIPropStr); ZeroMemory(pDIPropStr, sizeof(DIPROPSTRING)); pDIPropStr->diph.dwSize = sizeof(DIPROPSTRING); pDIPropStr->diph.dwHeaderSize = sizeof(DIPROPHEADER); pDIPropStr->diph.dwHow = DIPH_DEVICE; BYTE n = nAssigned; LPTSTR lpStr = new (TCHAR[MAX_STR_LEN]); ASSERT (lpStr); // find and assign ID's while( n-- ) { if( SUCCEEDED(pAssigned[n]->fnDeviceInterface->GetProperty(DIPROP_INSTANCENAME, &pDIPropStr->diph)) ) { // Our buffer is Only soooo big... // besides, it just doesn't make sence to display Everything! if( wcslen(pDIPropStr->wsz) > STR_LEN_64 ) { pDIPropStr->wsz[60] = pDIPropStr->wsz[61] = pDIPropStr->wsz[62] = TEXT('.'); pDIPropStr->wsz[63] = TEXT('\0'); } #ifdef _UNICODE _tcscpy(lpStr, pDIPropStr->wsz); #else _tcscpy(lpStr, W2A(pDIPropStr->wsz)); #endif // Put the first bracket on... _tcscat(lpStr, TEXT(" (")); // Now, get the productname of the device! if( SUCCEEDED(pAssigned[n]->fnDeviceInterface->GetProperty(DIPROP_PRODUCTNAME, &pDIPropStr->diph)) ) { #ifdef _UNICODE _tcscat(lpStr, pDIPropStr->wsz); #else _tcscat(lpStr, W2A(pDIPropStr->wsz)); #endif } // put the end bracket on... _tcscat(lpStr, TEXT(")")); BYTE n1 = (BYTE)SendMessage(hCtrl, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR)lpStr); SendMessage(hCtrl, LB_SETITEMDATA, n1, pAssigned[n]->ID); } #ifdef _DEBUG else OutputDebugString(TEXT("JOY.CPL: Advanced.cpp: UpdateChangeListCtrl: GetProperty failed!\n")); #endif // _DEBUG } if( lpStr ) delete[] (lpStr); if( pDIPropStr ) delete (pDIPropStr); return(TRUE); } /////////////////////////////////////////////////////////////////////////////// // FUNCTION: itoa(BYTE n, LPTSTR lpStr) // // PARAMETERS: BYTE n - Number to be translated // LPTSTR lpStr - Buffer to recieve translated value // // PURPOSE: Convert BYTE values < 20 to strings. /////////////////////////////////////////////////////////////////////////////// void itoa(BYTE n, LPTSTR lpStr) { // designed for use with the CPL ONLY! // Only supports values < NUMJOYDEVS! if( n > NUMJOYDEVS ) { #ifdef _DEBUG OutputDebugString(TEXT("JOY.CPL: itoa: n > NUMJOYDEVS!\n")); #endif return; } lpStr[0] = n % 10 + '0'; if( n > 9 ) { // Reverse the string and send it back... lpStr[1] = lpStr[0]; lpStr[0] = '1'; lpStr[2] = '\0'; } else lpStr[1] = '\0'; } int CALLBACK CompareIDItems(LPARAM item1, LPARAM item2, LPARAM uDirection) { if( LOWORD(item1) == LOWORD(item2) ) return(0); short nRet = (LOWORD(item1) > LOWORD(item2)) ? 1 : -1; return(uDirection) ? nRet : (nRet < 0) ? 1 : -1; } void LaunchChange( HWND hTmp ) { // Don't allow if you're in USER mode! if( (nFlags & USER_MODE) ) return; iAdvItem = (short)::SendMessage(hAdvListCtrl, LVM_GETNEXTITEM, (WPARAM)(int)-1, MAKELPARAM(LVNI_SELECTED, 0)); nOldID = (BYTE)GetItemData(hAdvListCtrl, (BYTE)iAdvItem); if( nOldID & ID_NONE ) return; // valid returns are IDOK, IDCANCEL, and IDC_CHANGE_LIST if( IDCANCEL != DialogBox(ghInstance, (PTSTR)IDD_ADV_CHANGE, ghDlg, (DLGPROC)ChangeDialogProc) ) { // Yeah, it's not really a DEVICEARRIVAL... ::SendMessage(hTmp, WM_DEVICECHANGE, DBT_DEVICEARRIVAL, 0); ::PostMessage(hAdvListCtrl, LVM_ENSUREVISIBLE, (BYTE)iAdvItem, TRUE); SetFocus(hAdvListCtrl); SetListCtrlItemFocus(hAdvListCtrl, (BYTE)iAdvItem); } } /////////////////////////////////////////////////////////////////////////////// // FUNCTION: SortTextItems( CListCtrl *pCtrl, short nCol, BOOL bAscending, int low, int high ) // // SortTextItems - Sort the list based on column text // // PARAMETERS: // pCtrl - pointer to list to sort // nCol - column that contains the text to be sorted // bAscending - indicate sort order // low - row to start scanning from - default row is 0 // high - row to end scan. -1 indicates last row // // PURPOSE: Sort text items in ListCtrl // Returns - Returns true for success /////////////////////////////////////////////////////////////////////////////// BOOL SortTextItems( CListCtrl *pCtrl, short nCol, BOOL bAscending, short low, short high ) { CHeaderCtrl* pHeader = (CHeaderCtrl*) pCtrl->GetDlgItem(0); if( nCol >= pHeader->GetItemCount() ) return(FALSE); if( high == -1 ) high = pCtrl->GetItemCount() - 1; short lo = low; short hi = high; if( hi <= lo ) return(FALSE); // The choices here are to malloc a buffer large enough for the largest // string, or malloc an LV_ITEM struct to get the length, then malloc // just the size we need. CString midItem = pCtrl->GetItemText( (lo+hi)/2, nCol ); // loop through the list until indices cross while( lo <= hi ) { // rowText will hold all column text for one row CStringArray rowText; // find the first element that is greater than or equal to // the partition element starting from the left Index. if( bAscending ) while( ( lo < high ) && ( pCtrl->GetItemText(lo, nCol) < midItem ) ) ++lo; else while( ( lo < high ) && ( pCtrl->GetItemText(lo, nCol) > midItem ) ) ++lo; // find an element that is smaller than or equal to // the partition element starting from the right Index. if( bAscending ) while( ( hi > low ) && ( pCtrl->GetItemText(hi, nCol) > midItem ) ) --hi; else while( ( hi > low ) && ( pCtrl->GetItemText(hi, nCol) < midItem ) ) --hi; // if the indexes have not crossed, swap // and if the items are not equal if( lo <= hi ) { // swap only if the items are not equal if( pCtrl->GetItemText(lo, nCol) != pCtrl->GetItemText(hi, nCol) ) { // swap the rows LV_ITEM lvitemlo, lvitemhi; BYTE nColCount = (BYTE)pHeader->GetItemCount(); rowText.SetSize( nColCount ); for( BYTE i = 0; i < nColCount; i++ ) rowText[i] = pCtrl->GetItemText(lo, i); lvitemlo.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; lvitemlo.iItem = lo; lvitemlo.iSubItem = 0; lvitemlo.stateMask = LVIS_CUT | LVIS_DROPHILITED | LVIS_FOCUSED | LVIS_SELECTED | LVIS_OVERLAYMASK | LVIS_STATEIMAGEMASK; lvitemhi = lvitemlo; lvitemhi.iItem = hi; ListView_GetItem(pCtrl->GetSafeHwnd(), &lvitemlo ); ListView_GetItem(pCtrl->GetSafeHwnd(), &lvitemhi ); for( i=0; iSetItemText(lo, i, pCtrl->GetItemText(hi, i)); lvitemhi.iItem = lo; ListView_SetItem(pCtrl->GetSafeHwnd(), &lvitemhi ); for( i=0; iSetItemText(hi, i, rowText[i]); lvitemlo.iItem = hi; ListView_SetItem(pCtrl->GetSafeHwnd(), &lvitemlo ); } ++lo; --hi; } } // If the right index has not reached the left side of array // must now sort the left partition. if( low < hi ) SortTextItems( pCtrl, nCol, bAscending, low, hi); // If the left index has not reached the right side of array // must now sort the right partition. if( lo < high ) SortTextItems( pCtrl, nCol, bAscending, lo, high); return(TRUE); } /////////////////////////////////////////////////////////////////////////////// // FUNCTION: EditSubLabel( BYTE nItem, BYTE nCol ) // // PARAMETERS: // EditSubLabel - Start edit of a sub item label // Returns - Temporary pointer to the new edit control // nItem - The row index of the item to edit // nCol - The column of the sub item. // PURPOSE: Provide editing services for any column in a CListCtrl /////////////////////////////////////////////////////////////////////////////// void EditSubLabel( BYTE nItem, BYTE nCol ) { #ifdef _DEBUG // Make sure that the item is visible if( !SendMessage(hAdvListCtrl, LVM_ENSUREVISIBLE, nItem, TRUE ) ) { OutputDebugString(TEXT("JOY.CPL: ADVANCED.CPP: EditSubLabel: requested item not visible!\n")); return; } #endif // _DEBUG // Get the column offset short offset = 0; BYTE i = 0; // OK, so here's what we have to do... // Traverse the columns incrementing the widths of the ones lesser than the one we're looking for! HDITEM *phdItem = new (HDITEM); ASSERT (phdItem); phdItem->mask = HDI_ORDER | HDI_WIDTH; HWND hHeader = GetDlgItem(hAdvListCtrl, 0); BYTE nColumns = (BYTE)::SendMessage(hHeader, HDM_GETITEMCOUNT, 0, 0L); BYTE nColWidth; do { ::SendMessage(hHeader, HDM_GETITEM, (WPARAM)(int)--nColumns, (LPARAM)(LPHDITEM)phdItem); if( phdItem->iOrder < nCol ) offset += (short)phdItem->cxy; if( phdItem->iOrder == nCol ) nColWidth = (BYTE)phdItem->cxy; } while( nColumns ); if( phdItem ) delete (phdItem); RECT rect; ListView_GetItemRect(hAdvListCtrl, nItem, &rect, LVIR_BOUNDS ); // Now scroll if we need to expose the column CRect rcClient; ::GetClientRect(hAdvListCtrl, &rcClient); if( offset + rect.left < 0 || offset + rect.left > rcClient.right ) { ::SendMessage(hAdvListCtrl, LVM_SCROLL, (WPARAM)(int)offset + rect.left, (LPARAM)(int)0); rect.left -= (offset + rect.left); } rect.left += offset+4; rect.right = rect.left + nColWidth - 3; // + ::SendMessage(hAdvListCtrl, LVM_GETCOLUMNWIDTH, (WPARAM)(int)nCol, (LPARAM)0L) - 3 ; if( rect.right > rcClient.right ) rect.right = rcClient.right; CEdit *pEdit = new CInPlaceEdit(nItem, 1); ASSERT (pEdit); // malloc the list ctrl CWnd *pListCtrl = new (CWnd); ASSERT (pListCtrl); pListCtrl->Attach(hAdvListCtrl); pEdit->Create(WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_CENTER, rect, pListCtrl, IDC_IPEDIT ); pListCtrl->Detach(); if( pListCtrl ) delete (pListCtrl); } #ifdef _UNICODE void SwapIDs(BYTE nSource, BYTE nTarget) { // malloc and retrieve the data from the selected item LPDIJOYCONFIG lpSelectedID = new (DIJOYCONFIG); ASSERT (lpSelectedID); ZeroMemory(lpSelectedID, sizeof(DIJOYCONFIG)); lpSelectedID->dwSize = sizeof(DIJOYCONFIG); // Get the Config of device on ID taken from Change List box! HRESULT hr = pDIJoyConfig->GetConfig(nSource, lpSelectedID, DIJC_ALL); if( hr == DIERR_NOTFOUND || hr == S_FALSE ) { // No Object on Selected ID! if( lpSelectedID ) delete (lpSelectedID); lpSelectedID = NULL; } // malloc and retrieve the data from the item associated // with the ID taken from the Item selected in the List box! LPDIJOYCONFIG lpSelectedItem = new (DIJOYCONFIG); ASSERT (lpSelectedItem); ZeroMemory(lpSelectedItem, sizeof(DIJOYCONFIG)); lpSelectedItem->dwSize = sizeof (DIJOYCONFIG); hr = pDIJoyConfig->GetConfig(nTarget, lpSelectedItem, DIJC_ALL); if( hr == DIERR_NOTFOUND || hr == S_FALSE ) { if( lpSelectedItem ) delete (lpSelectedItem); lpSelectedItem = NULL; } // *********************************************************** // Delete the configurations! // *********************************************************** // Set the hour glass SetCursor(LoadCursor(NULL, IDC_WAIT)); // ******************************************************** // OK, now... at this point you have: // lpSelectedID: containing NULL if the device wasn't present or... // a valid pointer containing the configuration of nSelectedID // lpSelected : containing NULL if the device wasn't present or... // a valid pointer containing the configuration of id // ******************************************************** // Time to Set the Configurations! if( lpSelectedID ) { hr = pDIJoyConfig->SetConfig(nTarget, lpSelectedID, DIJC_ALL); if( lpSelectedID ) delete (lpSelectedID); } else pDIJoyConfig->DeleteConfig(nSource); // delete both configurations // pointers will be NULL if the config was not found! if( lpSelectedItem ) { hr = pDIJoyConfig->SetConfig(nSource, lpSelectedItem, DIJC_ALL ); if( lpSelectedItem ) delete (lpSelectedItem); } pDIJoyConfig->SendNotify(); // Set the hour glass SetCursor(LoadCursor(NULL, IDC_ARROW)); } #else // Memphis version of SwapIDs... // Caution!!! Very Sensitive!!! void SwapIDs(BYTE nSelectedID, BYTE nSelectedItem) { // malloc and retrieve the data from the selected item LPDIJOYCONFIG lpSelectedID = new (DIJOYCONFIG); DWORD dwSelectedID = 0, dwSelectedItem = 0; ASSERT (lpSelectedID); ZeroMemory(lpSelectedID, sizeof(DIJOYCONFIG)); lpSelectedID->dwSize = sizeof(DIJOYCONFIG); // Get the Config of device on ID taken from Change List box! HRESULT hr = pDIJoyConfig->GetConfig(nSelectedID, lpSelectedID, DIJC_ALL); if( hr == DIERR_NOTFOUND || hr == S_FALSE ) { // No Object on Selected ID! if( lpSelectedID ) delete (lpSelectedID); lpSelectedID = NULL; } else { if( lpSelectedID ) delete (lpSelectedID); lpSelectedID = NULL; Error((short)IDS_DEST_ID_OCCUPIED_TITLE, (short)IDS_DEST_ID_OCCUPIED); return; } // malloc and retrieve the data from the item associated // with the ID taken from the Item selected in the List box! LPDIJOYCONFIG lpSelectedItem = new (DIJOYCONFIG); ASSERT (lpSelectedItem); ZeroMemory(lpSelectedItem, sizeof(DIJOYCONFIG)); lpSelectedItem->dwSize = sizeof (DIJOYCONFIG); hr = pDIJoyConfig->GetConfig(nSelectedItem, lpSelectedItem, DIJC_ALL); if( hr == DIERR_NOTFOUND || hr == S_FALSE ) { if( lpSelectedItem ) delete (lpSelectedItem); lpSelectedItem = NULL; return; } // *********************************************************** // Delete the configurations! // *********************************************************** // Set the hour glass SetCursor(LoadCursor(NULL, IDC_WAIT)); // ******************************************************** // OK, now... at this point you have: // lpSelectedID: containing NULL if the device wasn't present or... // a valid pointer containing the configuration of nSelectedID // lpSelected : containing NULL if the device wasn't present or... // a valid pointer containing the configuration of id // ******************************************************** // Time to Set the Configurations! if( lpSelectedID ) { DWORD dwFlags = DIJC_REGHWCONFIGTYPE | DIJC_CALLOUT | DIJC_GAIN | DIJC_GUIDINSTANCE; USES_CONVERSION; // if the callout is joyhid.vxd then the device is USB so... // OR on the DIJC_WDMGAMEPORT flag! if( !_stricmp(TEXT("joyhid.vxd"), W2A(lpSelectedID->wszCallout)) ) { // dwFlags |= DIJC_WDMGAMEPORT; dwSelectedID = 2; } else { dwSelectedID = 1; } if( dwSelectedID == 1 ) { hr = pDIJoyConfig->DeleteConfig(nSelectedID); /* * This notify is to fix the bug changing id on 2-axis 2-button joystick. */ pDIJoyConfig->SendNotify(); if( SUCCEEDED(hr) ) { hr = pDIJoyConfig->SetConfig(nSelectedItem, lpSelectedID, dwFlags ); if( SUCCEEDED(hr) ) { pDIJoyConfig->SendNotify(); } } } else { hr = pDIJoyConfig->SetConfig(nSelectedItem, lpSelectedID, dwFlags); pDIJoyConfig->SendNotify(); if( SUCCEEDED(hr) ) { hr = pDIJoyConfig->DeleteConfig(nSelectedID); pDIJoyConfig->SendNotify(); if( nSelectedID < nSelectedItem ) { pDIJoyConfig->SendNotify(); } } } if( lpSelectedID ) delete (lpSelectedID); } // delete both configurations // pointers will be NULL if the config was not found! if( lpSelectedItem ) { DWORD dwFlags = DIJC_REGHWCONFIGTYPE | DIJC_CALLOUT | DIJC_GAIN | DIJC_GUIDINSTANCE; USES_CONVERSION; if( _tcsicmp(TEXT("joyhid.vxd"), W2A(lpSelectedItem->wszCallout)) == 0 ) { // dwFlags |= DIJC_WDMGAMEPORT; dwSelectedItem = 2; //joyhid.vxd } #if 0 /* * Since MSGAME.VXD will directly write to the registry with some unhealthy data. * We have to change it before we move to other ID. */ else if( _tcsicmp(TEXT("MSGAME.VXD"), W2A(lpSelectedItem->wszCallout)) == 0 ) { lpSelectedItem->hwc.dwType += 1; dwSelectedItem = 3; //msgame.vxd (Sidewinder driver) } #endif else { dwSelectedItem = 1; //vjoyd.vxd } if( dwSelectedItem == 1 // VJOYD.VXD, // || dwSelectedItem == 3 ){ // MSGAME.VXD hr = pDIJoyConfig->DeleteConfig(nSelectedItem); /* * This notify is to fix the bug changing id on 2-axis 2-button joystick. */ pDIJoyConfig->SendNotify(); if( SUCCEEDED(hr) ) { hr = pDIJoyConfig->SetConfig(nSelectedID, lpSelectedItem, dwFlags ); if( SUCCEEDED(hr) ) { pDIJoyConfig->SendNotify(); } } } else { hr = pDIJoyConfig->SetConfig(nSelectedID, lpSelectedItem, dwFlags ); /* * This notify is to fix the bug changing id on 2-axis 2-button joystick. */ pDIJoyConfig->SendNotify(); if( SUCCEEDED(hr) ) { hr = pDIJoyConfig->DeleteConfig(nSelectedItem); pDIJoyConfig->SendNotify(); if( nSelectedID < nSelectedItem ) { pDIJoyConfig->SendNotify(); } } } if( lpSelectedItem ) { delete (lpSelectedItem); } } // Set the hour glass SetCursor(LoadCursor(NULL, IDC_ARROW)); } #endif