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.
2176 lines
51 KiB
2176 lines
51 KiB
//-----------------------------------------------------------------------------
|
|
// File: configwnd.cpp
|
|
//
|
|
// Desc: CConfigWnd is derived from CFlexWnd. It implements the top-level
|
|
// UI window which all other windows are descendents of.
|
|
//
|
|
// Functionalities handled by CConfigWnd are device tabs, Reset, Ok,
|
|
// and Cancel buttons.
|
|
//
|
|
// Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "common.hpp"
|
|
|
|
|
|
LPCTSTR g_tszAppWindowName = _T("DINPUT Default Mapper UI");
|
|
|
|
const int WINDOW_WIDTH = 640;
|
|
const int WINDOW_HEIGHT = 480;
|
|
const int TABTEXTMARGINLEFT = 7;
|
|
const int TABTEXTMARGINTOP = 3;
|
|
const int TABTEXTMARGINRIGHT = 7;
|
|
const int TABTEXTMARGINBOTTOM = 4;
|
|
const int BUTTONTEXTMARGINLEFT = 7;
|
|
const int BUTTONTEXTMARGINTOP = 3;
|
|
const int BUTTONTEXTMARGINRIGHT = 7;
|
|
const int BUTTONTEXTMARGINBOTTOM = 4;
|
|
const int BARBUTTONMARGINLEFT = 9;
|
|
const int BARBUTTONMARGINTOP = 4;
|
|
const int BARBUTTONMARGINRIGHT = 9;
|
|
const int BARBUTTONMARGINBOTTOM = 5;
|
|
const int BARBUTTONSPACING = 4;
|
|
|
|
//#define WM_QUERYACTIONASSIGNEDANYWHERE (WM_USER + 4)
|
|
|
|
|
|
CConfigWnd::CConfigWnd(CUIGlobals &uig) :
|
|
m_uig(uig),
|
|
m_bCreated(FALSE),
|
|
m_pPageFactory(NULL),
|
|
m_hPageFactoryInst(NULL),
|
|
m_pSurface(NULL),
|
|
m_pSurface3D(NULL),
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
m_bEditLayout(uig.QueryAllowEditLayout()),
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
m_CurSel(-1),
|
|
m_nCurGenre(0),
|
|
m_pbmTopGradient(NULL),
|
|
m_pbmBottomGradient(NULL),
|
|
m_pbmPointerEraser(NULL),
|
|
m_pbm3D(NULL),
|
|
m_p3DBits(NULL),
|
|
m_SurfFormat(D3DFMT_UNKNOWN),
|
|
m_uiPixelSize(4),
|
|
m_bBitmapsMapped(FALSE),
|
|
m_lpDI(NULL),
|
|
m_bScrollTabs(FALSE),
|
|
m_bScrollTabsLeft(FALSE),
|
|
m_bScrollTabsRight(FALSE),
|
|
m_nLeftTab(0),
|
|
m_dwInitFlags(0),
|
|
m_bHourGlass(FALSE),
|
|
m_bNeedRedraw(FALSE)
|
|
{
|
|
tracescope(__ts, _T("CConfigWnd::CConfigWnd()\n"));
|
|
m_lpDI = m_uig.GetDI();
|
|
|
|
m_pSurface = m_uig.GetSurface();
|
|
m_pSurface3D = m_uig.GetSurface3D();
|
|
|
|
if (m_pSurface != NULL || m_pSurface3D != NULL)
|
|
{
|
|
if (m_pSurface != NULL && m_pSurface3D != NULL)
|
|
{
|
|
etrace(_T("Both Surface and Surface3D are non-NULL, will use only Surface3D\n"));
|
|
|
|
m_pSurface->Release();
|
|
m_pSurface = NULL;
|
|
|
|
assert(m_pSurface3D != NULL);
|
|
assert(m_pSurface == NULL);
|
|
}
|
|
|
|
assert(m_pSurface != NULL || m_pSurface3D != NULL);
|
|
assert(!(m_pSurface != NULL && m_pSurface3D != NULL));
|
|
|
|
m_bRender3D = (m_pSurface3D != NULL);
|
|
|
|
SetRenderMode();
|
|
trace(_T("RenderMode set\n"));
|
|
traceBOOL(m_bRender3D);
|
|
|
|
if (m_bRender3D)
|
|
Create3DBitmap();
|
|
|
|
HDC hDC = GetRenderDC();
|
|
if (hDC != NULL)
|
|
{
|
|
m_pbmPointerEraser = CBitmap::Create(
|
|
GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),
|
|
hDC);
|
|
|
|
ReleaseRenderDC(hDC);
|
|
}
|
|
else
|
|
etrace(_T("Failed to get Render DC"));
|
|
}
|
|
}
|
|
|
|
CConfigWnd::~CConfigWnd()
|
|
{
|
|
tracescope(__ts, _T("CConfigWnd::~CConfigWnd()\n"));
|
|
ClearList();
|
|
|
|
if (m_lpDI != NULL)
|
|
m_lpDI->Release();
|
|
m_lpDI = NULL;
|
|
|
|
if (m_pSurface != NULL)
|
|
m_pSurface->Release();
|
|
m_pSurface = NULL;
|
|
|
|
if (m_pSurface3D != NULL)
|
|
m_pSurface3D->Release();
|
|
m_pSurface3D = NULL;
|
|
|
|
if (m_pPageFactory != NULL)
|
|
m_pPageFactory->Release();
|
|
m_pPageFactory = NULL;
|
|
|
|
if (m_hPageFactoryInst != NULL)
|
|
FreeLibrary(m_hPageFactoryInst);
|
|
m_hPageFactoryInst = NULL;
|
|
|
|
if (m_pbmPointerEraser != NULL)
|
|
delete m_pbmPointerEraser;
|
|
m_pbmPointerEraser = NULL;
|
|
|
|
if (m_pbm3D != NULL)
|
|
delete m_pbm3D;
|
|
m_pbm3D = NULL;
|
|
|
|
if (m_pbmTopGradient != NULL)
|
|
delete m_pbmTopGradient;
|
|
m_pbmTopGradient = NULL;
|
|
|
|
if (m_pbmBottomGradient != NULL)
|
|
delete m_pbmBottomGradient;
|
|
m_pbmBottomGradient = NULL;
|
|
}
|
|
|
|
HWND CMouseTrap::Create(HWND hParent, BOOL bInRenderMode)
|
|
{
|
|
if (m_hWnd)
|
|
return m_hWnd;
|
|
|
|
m_hParent = hParent;
|
|
int sx = GetSystemMetrics(SM_CXSCREEN);
|
|
int sy = GetSystemMetrics(SM_CYSCREEN);
|
|
RECT rect = {0, 0, sx, sy};
|
|
|
|
// If we are not in render mode, the trap window is exactly the same as the parent window
|
|
if (!bInRenderMode)
|
|
GetWindowRect(hParent, &rect);
|
|
|
|
return CFlexWnd::Create(
|
|
hParent,
|
|
NULL,
|
|
WS_EX_TOPMOST,
|
|
WS_POPUP | WS_VISIBLE,
|
|
rect);
|
|
}
|
|
|
|
BOOL CConfigWnd::Create(HWND hParent)
|
|
{
|
|
tracescope(__ts, _T("CConfigWnd::Create()\n"));
|
|
traceHEX(hParent);
|
|
|
|
HRESULT hresult = PrivGetClassObject(CLSID_CDIDeviceActionConfigPage, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (LPVOID*) &m_pPageFactory, &m_hPageFactoryInst);
|
|
if (FAILED(hresult))
|
|
{
|
|
// TODO: indicate failure to create page factory
|
|
m_pPageFactory = NULL;
|
|
m_hPageFactoryInst = NULL;
|
|
etrace1(_T("Failed to create page classfactory, PrivGetClassObject() returned 0x%08x\n"), hresult);
|
|
return FALSE;
|
|
}
|
|
|
|
int sx = GetSystemMetrics(SM_CXSCREEN);
|
|
int sy = GetSystemMetrics(SM_CYSCREEN);
|
|
int w = WINDOW_WIDTH;
|
|
int h = WINDOW_HEIGHT;
|
|
int rx = sx - w;
|
|
int ry = sy - h;
|
|
RECT rect = {rx / 2, ry / 2, 0, 0};
|
|
rect.right = rect.left + w;
|
|
rect.bottom = rect.top + h;
|
|
|
|
HWND hConfigParent = hParent;
|
|
|
|
if (InRenderMode())
|
|
{
|
|
hConfigParent = m_MouseTrap.Create(hParent, InRenderMode());
|
|
if (hConfigParent == NULL)
|
|
hConfigParent = hParent;
|
|
}
|
|
|
|
HWND hRet = CFlexWnd::Create(
|
|
hConfigParent,
|
|
g_tszAppWindowName,
|
|
0,
|
|
WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN,
|
|
rect);
|
|
|
|
if (hRet == NULL)
|
|
etrace(_T("CFlexWnd::Create() failed!\n"));
|
|
|
|
// Set the cursor extent to this window if we are in render mode (full-screen)
|
|
if (InRenderMode())
|
|
{
|
|
RECT rc;
|
|
GetWindowRect(m_hWnd, &rc);
|
|
ClipCursor(&rc);
|
|
}
|
|
|
|
return NULL != hRet;
|
|
}
|
|
|
|
void CConfigWnd::SetForegroundWindow()
|
|
{
|
|
// find the window
|
|
HWND hWnd = FindWindow(GetDefaultClassName(), g_tszAppWindowName);
|
|
|
|
// activate it if found
|
|
if (NULL != hWnd)
|
|
::SetForegroundWindow(hWnd);
|
|
}
|
|
|
|
void CConfigWnd::OnPaint(HDC hDC)
|
|
{
|
|
if (hDC == NULL)
|
|
return;
|
|
|
|
SIZE topsize = GetRectSize(m_rectTopGradient);
|
|
SIZE bottomsize = GetRectSize(m_rectBottomGradient);
|
|
SIZE bsize = {max(topsize.cx, bottomsize.cx),
|
|
max(topsize.cy, bottomsize.cy)};
|
|
CBitmap *pbm = CBitmap::Create(bsize, hDC);
|
|
if (pbm == NULL)
|
|
return;
|
|
HDC hBDC = NULL, hODC = hDC;
|
|
if (m_bHourGlass)
|
|
{
|
|
if (!InRenderMode())
|
|
{
|
|
// If not in fullscreen mode, change cursor to hourglass
|
|
HCURSOR hCursor;
|
|
hCursor = LoadCursor(NULL, IDC_WAIT);
|
|
SetCursor(hCursor);
|
|
} else
|
|
{
|
|
// If in fullscreen mode, hide the cursor during reset process.
|
|
SetCursor(NULL);
|
|
}
|
|
}
|
|
|
|
hBDC = pbm->BeginPaintInto(hDC);
|
|
if (hBDC == NULL)
|
|
{
|
|
delete pbm;
|
|
return;
|
|
}
|
|
hDC = hBDC;
|
|
|
|
if (m_pbmTopGradient != NULL)
|
|
m_pbmTopGradient->Draw(hDC);
|
|
|
|
{
|
|
CPaintHelper ph(m_uig, hDC);
|
|
|
|
ph.SetElement(UIE_BORDER);
|
|
|
|
ph.MoveTo(0, m_rectTopGradient.bottom - 1);
|
|
ph.LineTo(WINDOW_WIDTH, m_rectTopGradient.bottom - 1);
|
|
|
|
int i;
|
|
for (i = 0; i < GetNumElements(); i++)
|
|
{
|
|
const ELEMENT &e = GetElement(i);
|
|
BOOL bSel = i == m_CurSel;
|
|
|
|
ph.SetElement(bSel ? UIE_SELTAB : UIE_TAB);
|
|
|
|
ph.Rectangle(e.rect);
|
|
RECT trect = e.textrect;
|
|
DrawText(hDC, e.tszCaption, -1, &trect, DT_NOCLIP | DT_NOPREFIX);
|
|
|
|
if (bSel)
|
|
{
|
|
ph.SetPen(UIP_BLACK);
|
|
|
|
ph.MoveTo(e.rect.left + 1, e.rect.bottom - 1);
|
|
ph.LineTo(e.rect.right - 1, e.rect.bottom - 1);
|
|
}
|
|
}
|
|
|
|
if (m_bScrollTabs && GetNumElements() > 0)
|
|
{
|
|
ph.SetElement(UIE_TABARROW);
|
|
|
|
const ELEMENT &e = GetElement(0);
|
|
int h = e.rect.bottom - e.rect.top;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
RECT &rect = i == 0 ? m_rectSTRight : m_rectSTLeft;
|
|
BOOL bDraw = i ? m_bScrollTabsLeft : m_bScrollTabsRight;
|
|
ph.Rectangle(rect);
|
|
|
|
if (!bDraw)
|
|
continue;
|
|
|
|
int d,l,r,m,t,b, f = !i, w;
|
|
w = rect.right - rect.left;
|
|
|
|
l = f ? w / 4 : 3 * w / 8;
|
|
r = f ? 5 * w / 8 : 3 * w / 4;
|
|
d = r - l;
|
|
m = w / 2;
|
|
t = m - d;
|
|
b = m + d;
|
|
|
|
l += rect.left;
|
|
r += rect.left;
|
|
|
|
POINT p[4];
|
|
p[3].x = p[0].x = f ? l : r;
|
|
p[2].x = p[1].x = f ? r : l;
|
|
p[3].y = p[0].y = m;
|
|
p[1].y = t;
|
|
p[2].y = b;
|
|
|
|
Polyline(hDC, p, 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
pbm->Draw(hODC, topsize);
|
|
m_pbmBottomGradient->Draw(hDC);
|
|
|
|
{
|
|
CPaintHelper ph(m_uig, hDC);
|
|
|
|
ph.SetElement(UIE_BORDER);
|
|
|
|
Rectangle(hDC, 0, -1, WINDOW_WIDTH,
|
|
GetRectSize(m_rectBottomGradient).cy);
|
|
|
|
for (int i = 0; i < NUMBUTTONS; i++)
|
|
{
|
|
BOOL bOkOnly = !m_uig.InEditMode();
|
|
const BUTTON &b = m_Button[i];
|
|
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
BOOL bLayoutButton = i == BUTTON_LAYOUT;
|
|
|
|
if (!m_uig.QueryAllowEditLayout() && bLayoutButton)
|
|
continue;
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
|
|
if ( bOkOnly && i != BUTTON_CANCEL
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
&& !bLayoutButton
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
)
|
|
continue;
|
|
|
|
if (i == BUTTON_OK || bOkOnly)
|
|
ph.SetElement(UIE_DEFBUTTON);
|
|
else
|
|
ph.SetElement(UIE_BUTTON);
|
|
|
|
int ay = m_rectBottomGradient.top;
|
|
ph.Rectangle(b.rect.left, b.rect.top - ay, b.rect.right, b.rect.bottom - ay);
|
|
RECT trect = b.textrect;
|
|
OffsetRect(&trect, 0, -ay);
|
|
DrawText(hDC, b.tszCaption, -1, &trect, DT_NOCLIP | DT_NOPREFIX);
|
|
}
|
|
}
|
|
|
|
pbm->Draw(hODC, m_rectBottomGradient.left, m_rectBottomGradient.top, bottomsize);
|
|
|
|
pbm->EndPaintInto(hBDC);
|
|
delete pbm;
|
|
|
|
hDC = hODC;
|
|
|
|
{
|
|
CPaintHelper ph(m_uig, hDC);
|
|
|
|
ph.SetElement(UIE_BORDER);
|
|
|
|
ph.MoveTo(0, m_rectTopGradient.bottom);
|
|
ph.LineTo(0, m_rectBottomGradient.top);
|
|
|
|
ph.MoveTo(WINDOW_WIDTH - 1, m_rectTopGradient.bottom);
|
|
ph.LineTo(WINDOW_WIDTH - 1, m_rectBottomGradient.top);
|
|
}
|
|
}
|
|
|
|
void CConfigWnd::OnClick(POINT point, WPARAM fwKeys, BOOL bLeft)
|
|
{
|
|
int i;
|
|
|
|
// Un-highlight the current callout
|
|
SendMessage(CFlexWnd::s_CurrPageHwnd, WM_UNHIGHLIGHT, 0, 0);
|
|
|
|
// check scroll tab buttons
|
|
if (m_bScrollTabs)
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
RECT &r = !i ? m_rectSTRight : m_rectSTLeft;
|
|
BOOL b = !i ? m_bScrollTabsRight : m_bScrollTabsLeft;
|
|
if (PtInRect(&r, point))
|
|
{
|
|
if (b)
|
|
ScrollTabs(!i ? -1 : 1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// check tabs
|
|
for (i = 0; i < GetNumElements(); i++)
|
|
if (PtInRect(&(GetElement(i).rect), point))
|
|
{
|
|
// Check if the tab is partially obscured. If so we scroll the tab so it becomes completely visible.
|
|
POINT pt = {m_rectSTLeft.left, m_rectSTLeft.top};
|
|
if (m_bScrollTabsRight || m_bScrollTabsLeft)
|
|
{
|
|
while (PtInRect(&(GetElement(i).rect), pt))
|
|
ScrollTabs(1);
|
|
}
|
|
SelTab(i);
|
|
return;
|
|
}
|
|
|
|
// check buttons
|
|
for (i = 0; i < NUMBUTTONS; i++)
|
|
if (PtInRect(&(m_Button[i].rect), point))
|
|
{
|
|
FireButton(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void CConfigWnd::ScrollTabs(int by)
|
|
{
|
|
m_nLeftTab += by;
|
|
if (m_nLeftTab < 0)
|
|
m_nLeftTab = 0;
|
|
if (m_nLeftTab >= GetNumElements())
|
|
m_nLeftTab = GetNumElements() - 1;
|
|
CalcTabs();
|
|
Invalidate();
|
|
}
|
|
|
|
void CConfigWnd::OnDestroy()
|
|
{
|
|
tracescope(__ts, _T("CConfigWnd::OnDestroy()\n"));
|
|
ClipCursor(NULL); // Set cursor extent to entire desktop.
|
|
if (m_bCreated)
|
|
PostQuitMessage(EXIT_SUCCESS);
|
|
}
|
|
|
|
LRESULT CConfigWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
|
|
{
|
|
tracescope(__ts, _T("CConfigWnd::OnCreate()\n"));
|
|
|
|
if (!Init())
|
|
{
|
|
etrace(_T("CConfigWnd::Init() failed\n"));
|
|
return -1;
|
|
}
|
|
else
|
|
m_bCreated = TRUE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOL CALLBACK EnumDeviceCallback(const DIDEVICEINSTANCEW *lpdidi, LPDIRECTINPUTDEVICE8W pdiDev8W, DWORD dwFlags, DWORD dwDeviceRemaining, LPVOID pvRef)
|
|
{
|
|
if (pvRef != NULL)
|
|
return ((CConfigWnd *)pvRef)->EnumDeviceCallback(lpdidi);
|
|
else
|
|
return DIENUM_STOP;
|
|
}
|
|
|
|
BOOL CConfigWnd::EnumDeviceCallback(const DIDEVICEINSTANCEW *lpdidi)
|
|
{
|
|
DIDEVICEINSTANCEW didi;
|
|
didi.dwSize = sizeof(DIDEVICEINSTANCEW);
|
|
didi.guidInstance = lpdidi->guidInstance;
|
|
didi.guidProduct = lpdidi->guidProduct;
|
|
didi.dwDevType = lpdidi->dwDevType;
|
|
CopyStr(didi.tszInstanceName, lpdidi->tszInstanceName, MAX_PATH);
|
|
CopyStr(didi.tszProductName, lpdidi->tszProductName, MAX_PATH);
|
|
didi.guidFFDriver = lpdidi->guidFFDriver;
|
|
didi.wUsagePage = lpdidi->wUsagePage;
|
|
didi.wUsage = lpdidi->wUsage;
|
|
|
|
AddToList(&didi);
|
|
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
// show any error message here if returning false
|
|
BOOL CConfigWnd::Init(DWORD dwInitFlags)
|
|
{
|
|
tracescope(__ts, _T("CConfigWnd::Init()\n"));
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL bReInit = !!(dwInitFlags & CFGWND_INIT_REINIT);
|
|
|
|
m_dwInitFlags = dwInitFlags;
|
|
SetOnFunctionExit<DWORD> _set_m_dwInitFlags(m_dwInitFlags, 0);
|
|
|
|
// make sure we have DI
|
|
assert(m_lpDI != NULL);
|
|
if (m_lpDI == NULL)
|
|
{
|
|
etrace(_T("NULL m_lpDI\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(dwInitFlags & CFGWND_INIT_RESET))
|
|
{
|
|
// If we are not doing reset, clear device list then re-enumerate and rebuild.
|
|
|
|
// clear list
|
|
ClearList();
|
|
|
|
// enum devices
|
|
{
|
|
tracescope(ts, _T("Enumerating Devices...\n\n"));
|
|
|
|
DWORD dwFlags = DIEDBSFL_ATTACHEDONLY;
|
|
hr = m_lpDI->EnumDevicesBySemantics(NULL, (LPDIACTIONFORMATW)&m_uig.RefMasterAcFor(m_nCurGenre), ::EnumDeviceCallback, (LPVOID)this, dwFlags);
|
|
|
|
trace(_T("\n"));
|
|
}
|
|
} else
|
|
{
|
|
DIDEVICEINSTANCEW didiCopy;
|
|
// Saves a copy of device instance as the current ELEMENT will be freed by AddToList().
|
|
CopyMemory(&didiCopy, &GetElement(m_CurSel).didi, sizeof(didiCopy));
|
|
// If resetting, call AddToList with bReset as TRUE to just get default mappings.
|
|
AddToList(&didiCopy, TRUE);
|
|
}
|
|
|
|
// handle potential enum failure
|
|
if (FAILED(hr))
|
|
{
|
|
etrace1(_T("EnumDevicesBySemantics() failed, returning 0x%08x\n"), hr);
|
|
return FALSE;
|
|
}
|
|
|
|
// if there are no elements, fail
|
|
if (GetNumElements() < 1)
|
|
{
|
|
etrace(_T("No devices\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
// calculate tabs, buttons, init gradients
|
|
CalcTabs();
|
|
if (!bReInit)
|
|
{
|
|
CalcButtons();
|
|
InitGradients();
|
|
|
|
// set the timer
|
|
if (InRenderMode())
|
|
{
|
|
if (g_fptimeSetEvent)
|
|
g_fptimeSetEvent(20, 20, CConfigWnd::TimerProc,
|
|
(DWORD_PTR)m_hWnd, TIME_ONESHOT);
|
|
Render();
|
|
}
|
|
}
|
|
|
|
// make sure all the pages are in the right place
|
|
PlacePages();
|
|
|
|
// show the first page if we are not resetting. Show current page if we are.
|
|
int CurSel = (dwInitFlags & CFGWND_INIT_RESET) ? m_CurSel : 0;
|
|
m_CurSel = -1;
|
|
SelTab(CurSel);
|
|
|
|
// if we're already editting the layout, set it.
|
|
// KLUDGE, set false and toggle to set
|
|
if (m_bEditLayout)
|
|
{
|
|
m_bEditLayout = FALSE;
|
|
ToggleLayoutEditting();
|
|
}
|
|
|
|
trace(_T("\n"));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// This is called once for each device that will get configured.
|
|
int CConfigWnd::AddToList(const DIDEVICEINSTANCEW *lpdidi, BOOL bReset)
|
|
{
|
|
if (lpdidi == NULL)
|
|
{
|
|
etrace(_T("NULL lpdidi"));
|
|
assert(0);
|
|
return GetNumElements();
|
|
}
|
|
|
|
int i;
|
|
|
|
tracescope(ts, _T("Adding Device "));
|
|
trace(QSAFESTR(lpdidi->tszInstanceName));
|
|
trace(_T("\n\n"));
|
|
|
|
// add an element and get it if we are not doing reset (adding new device)
|
|
if (!bReset)
|
|
{
|
|
i = GetNumElements();
|
|
m_Element.SetSize(i + 1);
|
|
}
|
|
else
|
|
{
|
|
i = m_CurSel;
|
|
ClearElement(m_CurSel, bReset); // If resetting, clear the current ELEMENT as we will populate it below.
|
|
}
|
|
|
|
// If we are doing reset, then we use the existing ELEMENT that this device is already using.
|
|
ELEMENT &e = bReset ? GetElement(m_CurSel) : GetElement(i);
|
|
|
|
// set various needed variables
|
|
e.didi = *lpdidi;
|
|
e.bCalc = FALSE;
|
|
e.pUIGlobals = &m_uig;
|
|
|
|
// create and set the device
|
|
if (m_lpDI == NULL)
|
|
{
|
|
e.lpDID = NULL;
|
|
etrace(_T("m_lpDI NULL! Can't create this device.\n"));
|
|
}
|
|
else
|
|
{
|
|
e.lpDID = CreateDevice(e.didi.guidInstance);
|
|
if (!e.lpDID)
|
|
etrace(_T("Failed to create device!\n"));
|
|
}
|
|
|
|
if (!bReset)
|
|
{
|
|
// Find owner of device only if we are not doing reset.
|
|
// set starting current user index
|
|
DIPROPSTRING dips;
|
|
dips.diph.dwSize = sizeof(DIPROPSTRING);
|
|
dips.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
dips.diph.dwObj = DIPH_DEVICE;
|
|
dips.diph.dwHow = 0;
|
|
CopyStr(dips.wsz, "", MAX_PATH);
|
|
if (!e.lpDID)
|
|
{
|
|
etrace(_T("no lpDID, assuming device unassigned\n"));
|
|
e.nCurUser = -1;
|
|
}
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
else if (m_uig.QueryAllowEditLayout())
|
|
{
|
|
trace(_T("In DDK mode. Set user to 0 automatically.\n"));
|
|
e.nCurUser = 0;
|
|
}
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
else
|
|
{
|
|
HRESULT hr = e.lpDID->GetProperty(DIPROP_USERNAME, (LPDIPROPHEADER)&dips);
|
|
e.nCurUser = -1; // unassigned unless getusernameindex below works
|
|
if (FAILED(hr))
|
|
etrace(_T("GetProperty(DIPROP_USERNAME,...) failed\n"));
|
|
else if (hr == S_FALSE)
|
|
trace(_T("GetProperty(DIPROP_USERNAME,...) returned S_FALSE\n"));
|
|
else if (StrLen(dips.wsz) < 1)
|
|
trace(_T("GetProperty(DIPROP_USERNAME,...) returned empty string\n"));
|
|
else
|
|
{
|
|
trace(_T("Getting user name index for "));
|
|
traceWSTR(dips.wsz);
|
|
e.nCurUser = m_uig.GetUserNameIndex(dips.wsz);
|
|
trace(_T("Result: "));
|
|
traceLONG(e.nCurUser);
|
|
if (e.nCurUser == -1)
|
|
etrace(_T("Device assigned to user not passed to ConfigureDevices()\nConsidering unassigned now\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
// create and set the page object
|
|
HWND hwndChild = NULL;
|
|
e.pPage = CreatePageObject(i, e, hwndChild);
|
|
if (e.pPage == NULL)
|
|
etrace(_T("Failed to create page object!\n"));
|
|
e.hWnd = hwndChild;
|
|
if (e.hWnd == NULL)
|
|
etrace(_T("CreatePageObject() returned NULL hwnd!\n"));
|
|
|
|
// create/test the first acfor for this device with cur genre/user
|
|
traceLONG(m_nCurGenre);
|
|
traceLONG(e.nCurUser);
|
|
LPDIACTIONFORMATW lpAcFor = NULL;
|
|
if (e.nCurUser != -1)
|
|
{
|
|
lpAcFor = e.GetAcFor(m_nCurGenre, e.nCurUser, bReset);
|
|
if (lpAcFor != NULL)
|
|
TraceActionFormat(_T("Starting Device ActionFormat:"), *lpAcFor);
|
|
else
|
|
etrace(_T("Failed to create starting ActionFormat\n"));
|
|
}
|
|
else
|
|
trace(_T("Device unassigned\n"));
|
|
|
|
// check if anything was unsuccessful
|
|
if ((lpAcFor == NULL && e.nCurUser != -1) || e.lpDID == NULL || e.pPage == NULL || e.hWnd == NULL)
|
|
{
|
|
// clear what was successful, set the size back (remove element),
|
|
// and indicate error
|
|
ClearElement(e);
|
|
m_Element.SetSize(i);
|
|
etrace(_T("Can't add this device - Element removed\n"));
|
|
}
|
|
|
|
trace(_T("\n"));
|
|
|
|
return GetNumElements();
|
|
}
|
|
|
|
LPDIRECTINPUTDEVICE8W CConfigWnd::CreateDevice(GUID &guid)
|
|
{
|
|
LPDIRECTINPUTDEVICE8W lpDID;
|
|
|
|
HRESULT hr = m_lpDI->CreateDevice(guid, &lpDID, NULL);
|
|
if (FAILED(hr) || lpDID == NULL)
|
|
{
|
|
etrace2(_T("Could not create device (guid %s), CreateDevice() returned 0x%08x\n"), GUIDSTR(guid), hr);
|
|
return NULL;
|
|
}
|
|
|
|
return lpDID;
|
|
}
|
|
|
|
void CConfigWnd::ClearElement(int i, BOOL bReset)
|
|
{
|
|
ELEMENT &e = GetElement(i);
|
|
ClearElement(e, bReset);
|
|
}
|
|
|
|
void CConfigWnd::ClearElement(ELEMENT &e, BOOL bReset)
|
|
{
|
|
if (e.pPage != NULL)
|
|
DestroyPageObject(e.pPage);
|
|
if (e.lpDID != NULL)
|
|
{
|
|
e.lpDID->Release();
|
|
e.lpDID = NULL;
|
|
}
|
|
e.pPage = NULL;
|
|
e.lpDID = NULL;
|
|
e.hWnd = NULL;
|
|
e.pUIGlobals = NULL; // not freed
|
|
if (!bReset) // Free map only if we are not resetting (delete permanently).
|
|
e.FreeMap();
|
|
}
|
|
|
|
void CConfigWnd::ClearList()
|
|
{
|
|
int i;
|
|
for (i = 0; i < GetNumElements(); i++)
|
|
ClearElement(i);
|
|
m_Element.RemoveAll();
|
|
assert(!GetNumElements());
|
|
}
|
|
|
|
void CConfigWnd::PlacePages()
|
|
{
|
|
RECT rect;
|
|
GetPageRect(rect);
|
|
|
|
for (int i = 0; i < GetNumElements(); i++)
|
|
{
|
|
DWORD flags = SWP_NOZORDER | SWP_NOACTIVATE;
|
|
SetWindowPos(GetElement(i).hWnd, NULL,
|
|
rect.left, rect.top,
|
|
rect.right - rect.left,
|
|
rect.bottom - rect.top, flags);
|
|
}
|
|
}
|
|
|
|
SIZE CConfigWnd::GetTextSize(LPCTSTR tszText)
|
|
{
|
|
RECT trect = {0, 0, 1, 1};
|
|
HDC hDC = CreateCompatibleDC(NULL);
|
|
if (hDC != NULL)
|
|
{
|
|
{
|
|
CPaintHelper ph(m_uig, hDC);
|
|
ph.SetFont(UIF_FRAME);
|
|
DrawText(hDC, tszText, -1, &trect, DT_CALCRECT | DT_NOPREFIX);
|
|
}
|
|
DeleteDC(hDC);
|
|
}
|
|
SIZE size = {trect.right - trect.left, trect.bottom - trect.top};
|
|
return size;
|
|
}
|
|
|
|
void CConfigWnd::InitGradients()
|
|
{
|
|
if (m_pbmTopGradient == NULL)
|
|
m_pbmTopGradient = CBitmap::CreateHorzGradient(m_rectTopGradient, m_uig.GetColor(UIC_CONTROLFILL), m_uig.GetColor(UIC_CONTROLFILL));
|
|
if (m_pbmBottomGradient == NULL)
|
|
m_pbmBottomGradient = CBitmap::CreateHorzGradient(m_rectBottomGradient, m_uig.GetColor(UIC_CONTROLFILL), m_uig.GetColor(UIC_CONTROLFILL));
|
|
}
|
|
|
|
void CConfigWnd::CalcTabs()
|
|
{
|
|
int i, maxh = 0, lastx = 0;
|
|
for (i = 0; i < GetNumElements(); i++)
|
|
{
|
|
ELEMENT &e = GetElement(i);
|
|
CopyStr(e.tszCaption, e.didi.tszInstanceName, MAX_PATH);
|
|
e.rect.left = i > 0 ? GetElement(i - 1).rect.right - 1 : 0;
|
|
e.rect.top = 0;
|
|
SIZE tsize = GetTextSize(e.tszCaption);
|
|
e.textrect.left = e.textrect.top = 0;
|
|
e.textrect.right = tsize.cx;
|
|
e.textrect.bottom = tsize.cy;
|
|
OffsetRect(&e.textrect, e.rect.left + TABTEXTMARGINLEFT, e.rect.top + TABTEXTMARGINTOP);
|
|
int w = tsize.cx;
|
|
int h = tsize.cy;
|
|
e.rect.right = e.rect.left + TABTEXTMARGINLEFT + w + TABTEXTMARGINRIGHT + 1;
|
|
e.rect.bottom = e.rect.top + TABTEXTMARGINTOP + h + TABTEXTMARGINBOTTOM;
|
|
h = e.rect.bottom - e.rect.top;
|
|
if (h > maxh) maxh = h;
|
|
e.bCalc = TRUE;
|
|
}
|
|
|
|
for (i = 0; i < GetNumElements(); i++)
|
|
{
|
|
ELEMENT &e = GetElement(i);
|
|
e.rect.bottom = e.rect.top + maxh;
|
|
lastx = e.rect.right;
|
|
}
|
|
|
|
if (lastx > WINDOW_WIDTH)
|
|
{
|
|
if (!m_bScrollTabs)
|
|
m_nLeftTab = 0;
|
|
m_bScrollTabs = TRUE;
|
|
}
|
|
else
|
|
{
|
|
m_bScrollTabs = FALSE;
|
|
m_nLeftTab = 0;
|
|
}
|
|
|
|
int cutoff = WINDOW_WIDTH;
|
|
if (m_bScrollTabs)
|
|
{
|
|
cutoff = WINDOW_WIDTH - maxh * 2;
|
|
RECT r = {WINDOW_WIDTH - maxh, 0, WINDOW_WIDTH, maxh};
|
|
m_rectSTLeft = r;
|
|
OffsetRect(&r, -(maxh - 1), 0);
|
|
m_rectSTRight = r;
|
|
}
|
|
|
|
if (m_bScrollTabs && m_nLeftTab > 0)
|
|
{
|
|
int left = GetElement(m_nLeftTab).rect.left, right = 0;
|
|
for (i = 0; i < GetNumElements(); i++)
|
|
{
|
|
ELEMENT &e = GetElement(i);
|
|
OffsetRect(&e.rect, -left, 0);
|
|
OffsetRect(&e.textrect, -left, 0);
|
|
if (e.rect.right > right)
|
|
right = e.rect.right;
|
|
}
|
|
lastx = right;
|
|
}
|
|
|
|
if (m_bScrollTabs)
|
|
{
|
|
m_bScrollTabsLeft = lastx > cutoff && m_nLeftTab < GetNumElements() - 1;
|
|
m_bScrollTabsRight = m_nLeftTab > 0;
|
|
}
|
|
|
|
RECT t = {0/*lastx*/, 0, WINDOW_WIDTH, maxh};
|
|
m_rectTopGradient = t;
|
|
}
|
|
|
|
void CConfigWnd::CalcButtons()
|
|
{
|
|
SIZE max = {0, 0};
|
|
int i;
|
|
for (i = 0; i < NUMBUTTONS; i++)
|
|
{
|
|
BUTTON &b = m_Button[i];
|
|
|
|
if (!StrLen(b.tszCaption))
|
|
{
|
|
switch (i)
|
|
{
|
|
case BUTTON_RESET:
|
|
LoadString(g_hModule, IDS_BUTTON_RESET, b.tszCaption, MAX_PATH);
|
|
break;
|
|
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
case BUTTON_LAYOUT:
|
|
LoadString(g_hModule, IDS_BUTTON_LAYOUT, b.tszCaption, MAX_PATH);
|
|
break;
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
|
|
case BUTTON_CANCEL:
|
|
if (m_uig.InEditMode())
|
|
{
|
|
LoadString(g_hModule, IDS_BUTTON_CANCEL, b.tszCaption, MAX_PATH);
|
|
break;
|
|
}
|
|
// else, intentional fallthrough
|
|
|
|
case BUTTON_OK:
|
|
LoadString(g_hModule, IDS_BUTTON_OK, b.tszCaption, MAX_PATH);
|
|
break;
|
|
}
|
|
}
|
|
|
|
b.textsize = GetTextSize(b.tszCaption);
|
|
|
|
if (b.textsize.cx > max.cx)
|
|
max.cx = b.textsize.cx;
|
|
if (b.textsize.cy > max.cy)
|
|
max.cy = b.textsize.cy;
|
|
}
|
|
|
|
max.cx += BUTTONTEXTMARGINLEFT + BUTTONTEXTMARGINRIGHT;
|
|
max.cy += BUTTONTEXTMARGINTOP + BUTTONTEXTMARGINBOTTOM;
|
|
|
|
m_rectBottomGradient.bottom = WINDOW_HEIGHT;
|
|
m_rectBottomGradient.top = m_rectBottomGradient.bottom - max.cy - BARBUTTONMARGINTOP - BARBUTTONMARGINBOTTOM;
|
|
m_rectBottomGradient.left = 0;
|
|
m_rectBottomGradient.right = WINDOW_WIDTH;
|
|
|
|
for (i = 0; i < NUMBUTTONS; i++)
|
|
{
|
|
BUTTON &b = m_Button[i];
|
|
|
|
RECT z = {0,0,0,0};
|
|
|
|
b.rect = z;
|
|
b.rect.right = max.cx;
|
|
b.rect.bottom = max.cy;
|
|
|
|
int by = m_rectBottomGradient.top + BARBUTTONMARGINTOP;
|
|
|
|
switch (i)
|
|
{
|
|
case BUTTON_RESET:
|
|
OffsetRect(&b.rect, BARBUTTONMARGINLEFT, by);
|
|
break;
|
|
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
case BUTTON_LAYOUT:
|
|
OffsetRect(&b.rect, WINDOW_WIDTH / 2 - max.cx / 2, by);
|
|
break;
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
|
|
case BUTTON_CANCEL:
|
|
OffsetRect(&b.rect,
|
|
m_rectBottomGradient.right - BARBUTTONMARGINRIGHT - max.cx, by);
|
|
break;
|
|
|
|
case BUTTON_OK:
|
|
OffsetRect(&b.rect,
|
|
m_rectBottomGradient.right - BARBUTTONMARGINRIGHT - max.cx - max.cx - BARBUTTONSPACING, by);
|
|
break;
|
|
}
|
|
|
|
POINT m = {(b.rect.right + b.rect.left) / 2, (b.rect.bottom + b.rect.top) / 2};
|
|
b.textrect.left = m.x - b.textsize.cx / 2;
|
|
b.textrect.top = m.y - b.textsize.cy / 2;
|
|
b.textrect.right = b.textrect.left + b.textsize.cx;
|
|
b.textrect.bottom = b.textrect.top + b.textsize.cy;
|
|
}
|
|
}
|
|
|
|
void CConfigWnd::GetPageRect(RECT &rect, BOOL bTemp)
|
|
{
|
|
if (bTemp)
|
|
{
|
|
rect.left = 1;
|
|
rect.right = WINDOW_WIDTH - 1;
|
|
rect.top = 40;
|
|
rect.bottom = WINDOW_HEIGHT - 40;
|
|
}
|
|
else
|
|
{
|
|
rect.left = 1;
|
|
rect.right = WINDOW_WIDTH - 1;
|
|
rect.top = m_rectTopGradient.bottom;
|
|
rect.bottom = m_rectBottomGradient.top;
|
|
}
|
|
}
|
|
|
|
void CConfigWnd::ToggleLayoutEditting()
|
|
{
|
|
m_bEditLayout = !m_bEditLayout;
|
|
|
|
for (int i = 0; i < GetNumElements(); i++)
|
|
{
|
|
ELEMENT &e = GetElement(i);
|
|
if (e.pPage)
|
|
e.pPage->SetEditLayout(m_bEditLayout);
|
|
}
|
|
}
|
|
|
|
void CConfigWnd::FireButton(int b)
|
|
{
|
|
switch(b)
|
|
{
|
|
case BUTTON_OK:
|
|
if (!m_uig.InEditMode())
|
|
break; // If not in edit mode, Ok button doesn't not exist so we shouldn't do anything.
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
if (m_uig.QueryAllowEditLayout())
|
|
{
|
|
int MsgBoxRet = MessageBox(m_hWnd, _T("Do you wish to save layout information?"), _T("Save"), MB_YESNOCANCEL);
|
|
if (MsgBoxRet == IDYES)
|
|
{
|
|
// Write IHV settings for all devices.
|
|
for (int i = 0; i < GetNumElements(); i++)
|
|
GetElement(i).pPage->WriteIHVSetting();
|
|
}
|
|
else
|
|
if (MsgBoxRet == IDCANCEL)
|
|
break;
|
|
} else
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
Apply(); // If we are in Edit Layout mode, do not call Apply() to save to user setting.
|
|
// intentional fallthrough
|
|
|
|
case BUTTON_CANCEL:
|
|
Destroy();
|
|
break;
|
|
|
|
case BUTTON_RESET:
|
|
if (m_uig.InEditMode()) // Only reset if in edit mode. Do nothing in view mode.
|
|
Reset();
|
|
break;
|
|
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
case BUTTON_LAYOUT:
|
|
if (m_uig.QueryAllowEditLayout())
|
|
ToggleLayoutEditting();
|
|
break;
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CConfigWnd::SelTab(int i)
|
|
{
|
|
if (i >= 0 && i < GetNumElements())
|
|
{
|
|
if (i == m_CurSel)
|
|
return;
|
|
ShowPage(i);
|
|
HidePage(m_CurSel);
|
|
m_CurSel = i;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
PAGETYPE *CConfigWnd::CreatePageObject(int nPage, const ELEMENT &e, HWND &refhChildWnd)
|
|
{
|
|
if (m_pPageFactory == NULL)
|
|
return NULL;
|
|
|
|
PAGETYPE *pPage = NULL;
|
|
HRESULT hresult = m_pPageFactory->CreateInstance(NULL, IID_IDIDeviceActionConfigPage, (LPVOID*) &pPage);
|
|
if (FAILED(hresult) || pPage == NULL)
|
|
return NULL;
|
|
|
|
DICFGPAGECREATESTRUCT cs;
|
|
cs.dwSize = sizeof(DICFGPAGECREATESTRUCT);
|
|
cs.nPage = nPage;
|
|
cs.hParentWnd = m_hWnd;
|
|
GetPageRect(cs.rect, TRUE);
|
|
cs.hPageWnd = NULL;
|
|
cs.didi = e.didi;
|
|
cs.lpDID = e.lpDID;
|
|
cs.pUIGlobals = &m_uig;
|
|
cs.pUIFrame = dynamic_cast<IDIConfigUIFrameWindow *>(this);
|
|
|
|
hresult = pPage->Create(&cs);
|
|
if (FAILED(hresult))
|
|
{
|
|
etrace1(_T("pPage->Create() failed, returning 0x%08x\n"), hresult);
|
|
pPage->Release();
|
|
return NULL;
|
|
}
|
|
|
|
refhChildWnd = cs.hPageWnd;
|
|
|
|
return pPage;
|
|
}
|
|
|
|
void CConfigWnd::DestroyPageObject(PAGETYPE *&pPage)
|
|
{
|
|
if (pPage != NULL)
|
|
pPage->Release();
|
|
pPage = NULL;
|
|
}
|
|
|
|
void CConfigWnd::ShowPage(int i)
|
|
{
|
|
if (i == -1)
|
|
return;
|
|
|
|
if (i < 0 || i >= GetNumElements())
|
|
{
|
|
assert(0);
|
|
return;
|
|
}
|
|
|
|
ELEMENT &e = GetElement(i);
|
|
|
|
PAGETYPE *pPage = e.pPage;
|
|
if (pPage == NULL)
|
|
{
|
|
assert(0);
|
|
return;
|
|
}
|
|
|
|
pPage->Show(e.GetAcFor(m_nCurGenre, e.nCurUser));
|
|
}
|
|
|
|
void CConfigWnd::HidePage(int i)
|
|
{
|
|
if (i == -1)
|
|
return;
|
|
|
|
if (i < 0 || i >= GetNumElements())
|
|
{
|
|
assert(0);
|
|
return;
|
|
}
|
|
|
|
PAGETYPE *pPage = GetElement(i).pPage;
|
|
if (pPage == NULL)
|
|
{
|
|
assert(0);
|
|
return;
|
|
}
|
|
|
|
pPage->Hide();
|
|
}
|
|
|
|
void CConfigWnd::OnMouseOver(POINT point, WPARAM fwKeys)
|
|
{
|
|
int i;
|
|
|
|
CFlexWnd::s_ToolTip.SetEnable(FALSE);
|
|
|
|
// check scroll tab buttons
|
|
if (m_bScrollTabs)
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
RECT &r = !i ? m_rectSTRight : m_rectSTLeft;
|
|
BOOL b = !i ? m_bScrollTabsRight : m_bScrollTabsLeft;
|
|
if (PtInRect(&r, point))
|
|
{
|
|
if (b)
|
|
GetElement(m_CurSel).pPage->SetInfoText(m_uig.InEditMode() ? IDS_INFOMSG_EDIT_TABSCROLL : IDS_INFOMSG_VIEW_TABSCROLL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// check tabs
|
|
for (i = 0; i < GetNumElements(); i++)
|
|
if (PtInRect(&(GetElement(i).rect), point))
|
|
{
|
|
GetElement(m_CurSel).pPage->SetInfoText(m_uig.InEditMode() ? IDS_INFOMSG_EDIT_TAB : IDS_INFOMSG_VIEW_TAB);
|
|
return;
|
|
}
|
|
|
|
// check buttons
|
|
for (i = 0; i < NUMBUTTONS; i++)
|
|
if (PtInRect(&(m_Button[i].rect), point))
|
|
{
|
|
switch(i)
|
|
{
|
|
case BUTTON_OK:
|
|
if (m_uig.InEditMode())
|
|
GetElement(m_CurSel).pPage->SetInfoText(IDS_INFOMSG_EDIT_OK);
|
|
break;
|
|
|
|
case BUTTON_CANCEL:
|
|
if (m_uig.InEditMode())
|
|
GetElement(m_CurSel).pPage->SetInfoText(IDS_INFOMSG_EDIT_CANCEL);
|
|
else
|
|
GetElement(m_CurSel).pPage->SetInfoText(IDS_INFOMSG_VIEW_OK);
|
|
break;
|
|
|
|
case BUTTON_RESET:
|
|
if (m_uig.InEditMode()) // Only reset if in edit mode. Do nothing in view mode.
|
|
GetElement(m_CurSel).pPage->SetInfoText(IDS_INFOMSG_EDIT_RESET);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
GetElement(m_CurSel).pPage->SetInfoText(-1);
|
|
}
|
|
|
|
void CALLBACK CConfigWnd::TimerProc(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
|
|
{
|
|
if (!IsWindow((HWND)dwUser)) return; // Verify that dwUser is a valid window handle
|
|
CConfigWnd *pCfgWnd = (CConfigWnd *)GetFlexWnd((HWND)dwUser); // Get flex object
|
|
|
|
// We use PostMessage instead of calling Render() so we stay synchronized.
|
|
PostMessage((HWND)dwUser, WM_DIRENDER, 0, 0);
|
|
}
|
|
|
|
void CConfigWnd::MapBitmaps(HDC hDC)
|
|
{
|
|
if (m_bBitmapsMapped)
|
|
return;
|
|
|
|
if (m_pbmTopGradient)
|
|
m_pbmTopGradient->MapToDC(hDC);
|
|
if (m_pbmBottomGradient)
|
|
m_pbmBottomGradient->MapToDC(hDC);
|
|
|
|
m_bBitmapsMapped = TRUE;
|
|
}
|
|
|
|
LPDIACTIONFORMATW CConfigWnd::GetCurAcFor(ELEMENT &e)
|
|
{
|
|
return e.GetAcFor(m_nCurGenre, e.nCurUser);
|
|
}
|
|
|
|
BOOL CConfigWnd::IsActionAssignedAnywhere(GUID GuidInstance, int nActionIndex)
|
|
{
|
|
// Find out which user owns the device in question first.
|
|
int nUser = 0;
|
|
for (int ii = 0; ii < GetNumElements(); ii++)
|
|
{
|
|
ELEMENT &e = GetElement(ii);
|
|
if (IsEqualGUID(e.didi.guidInstance, GuidInstance))
|
|
{
|
|
nUser = e.nCurUser;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Now we check the actions against this user.
|
|
for (int i = 0; i < GetNumElements(); i++)
|
|
{
|
|
ELEMENT &e = GetElement(i);
|
|
const LPDIACTIONFORMATW &lpAcFor = e.GetAcFor(m_nCurGenre, nUser);
|
|
|
|
if (lpAcFor == NULL)
|
|
continue;
|
|
|
|
if (nActionIndex < 0 || nActionIndex > int(lpAcFor->dwNumActions))
|
|
continue;
|
|
|
|
// If this device is not owned by this user, don't need to check.
|
|
if (e.nCurUser != nUser)
|
|
continue;
|
|
|
|
const DIACTIONW &a = lpAcFor->rgoAction[nActionIndex];
|
|
|
|
if (!IsEqualGUID(a.guidInstance, GUID_NULL))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
LRESULT CConfigWnd::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_ACTIVATE:
|
|
switch(wParam)
|
|
{
|
|
case WA_ACTIVE:
|
|
case WA_CLICKACTIVE:
|
|
// Set the cursor extent to this window if we are in render mode because the
|
|
// cursor can't be drawn by us when it's not above us.
|
|
if (InRenderMode())
|
|
{
|
|
RECT rc;
|
|
GetWindowRect(m_hWnd, &rc);
|
|
ClipCursor(&rc);
|
|
}
|
|
// Reacquire current device
|
|
if (GetNumElements() && m_CurSel >= 0)
|
|
GetElement(m_CurSel).pPage->Reacquire();
|
|
break;
|
|
case WA_INACTIVE:
|
|
// Unacquire current device
|
|
if (GetNumElements() && m_CurSel >= 0)
|
|
GetElement(m_CurSel).pPage->Unacquire();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_DIRENDER:
|
|
// Render message, sent by TimerProc() earlier.
|
|
// The timer proc has request a render operation.
|
|
Render(m_bNeedRedraw);
|
|
|
|
// Set the next timer event.
|
|
if (g_fptimeSetEvent)
|
|
g_fptimeSetEvent(20, 20, CConfigWnd::TimerProc,
|
|
(DWORD_PTR)m_hWnd, TIME_ONESHOT);
|
|
return 0;
|
|
|
|
case WM_SETFOCUS:
|
|
// Set the keyboard focus to the current page window.
|
|
ShowPage(m_CurSel); // Call Show() on current page so it can get keyboard focus.
|
|
return 0;
|
|
|
|
// WM_NCHITTEST handler is added to support moving window when in GDI mode.
|
|
case WM_NCHITTEST:
|
|
{
|
|
if (InRenderMode()) break;
|
|
|
|
BOOL bHitCaption = TRUE;
|
|
POINT point = {(short)LOWORD(lParam), (short)HIWORD(lParam)};
|
|
int i;
|
|
|
|
ScreenToClient(m_hWnd, &point);
|
|
// check scroll tab buttons
|
|
if (m_bScrollTabs)
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
RECT &r = !i ? m_rectSTRight : m_rectSTLeft;
|
|
BOOL b = !i ? m_bScrollTabsRight : m_bScrollTabsLeft;
|
|
if (PtInRect(&r, point))
|
|
{
|
|
if (b)
|
|
bHitCaption = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check tabs
|
|
for (i = 0; i < GetNumElements(); i++)
|
|
if (PtInRect(&(GetElement(i).rect), point))
|
|
{
|
|
bHitCaption = FALSE;
|
|
break;
|
|
}
|
|
|
|
// check buttons
|
|
for (i = 0; i < NUMBUTTONS; i++)
|
|
if (PtInRect(&(m_Button[i].rect), point))
|
|
{
|
|
if ((i == BUTTON_RESET || i == BUTTON_OK) && !m_uig.InEditMode()) continue;
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
if (i == BUTTON_LAYOUT && !m_uig.QueryAllowEditLayout()) continue;
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
bHitCaption = FALSE;
|
|
break;
|
|
}
|
|
|
|
// Check Y coordinate to see if it is within the caption bar.
|
|
if ((point.y < GetElement(0).rect.top || point.y > GetElement(0).rect.bottom) &&
|
|
(point.y < m_rectBottomGradient.top || point.y > m_rectBottomGradient.bottom))
|
|
bHitCaption = FALSE;
|
|
|
|
if (bHitCaption)
|
|
{
|
|
// If we are returning HTCAPTION, clear the page's info box.
|
|
GetElement(m_CurSel).pPage->SetInfoText(-1);
|
|
return HTCAPTION;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_CFGUIRESET:
|
|
{
|
|
CFlexWnd::s_ToolTip.SetEnable(FALSE);
|
|
m_bHourGlass = TRUE; // Set the flag so Render() will draw hourglass instead of arrow
|
|
Invalidate();
|
|
SendMessage(this->m_hWnd, WM_PAINT, 0, 0);
|
|
if (InRenderMode()) // If in render mode, need to specifically call OnRender as sending WM_PAINT merely changes flag.
|
|
Render(TRUE);
|
|
if (!Init(CFGWND_INIT_REINIT | CFGWND_INIT_RESET))
|
|
{
|
|
m_uig.SetFinalResult(E_FAIL);
|
|
Destroy();
|
|
}
|
|
m_bHourGlass = FALSE; // Change cursor back to arrow
|
|
m_MsgBox.Destroy();
|
|
Invalidate();
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_SETCURSOR:
|
|
{
|
|
static HCURSOR hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
::SetCursor(InRenderMode() ? NULL : hCursor);
|
|
}
|
|
return TRUE;
|
|
|
|
// case WM_QUERYACTIONASSIGNEDANYWHERE:
|
|
// return IsActionAssignedAnywhere(int(wParam), int(lParam));
|
|
}
|
|
|
|
return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
|
|
}
|
|
|
|
HRESULT CConfigWnd::Apply()
|
|
{
|
|
tracescope(ts, _T("\n\nApplying Changes to All Devices...\n"));
|
|
|
|
// Devices need to be in the unaquired state when SetActionMap is called.
|
|
Unacquire();
|
|
|
|
for (int i = 0; i < GetNumElements(); i++)
|
|
GetElement(i).Apply();
|
|
|
|
Reacquire();
|
|
|
|
trace(_T("\n\n"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
int CConfigWnd::GetNumElements()
|
|
{
|
|
return m_Element.GetSize();
|
|
}
|
|
|
|
ELEMENT &CConfigWnd::GetElement(int i)
|
|
{
|
|
if (i < 0 || i >= GetNumElements())
|
|
{
|
|
assert(0);
|
|
etrace1(_T("Tried to get invalid element %d\n"), i);
|
|
return m_InvalidElement;
|
|
}
|
|
|
|
return m_Element[i];
|
|
}
|
|
|
|
// This function returns a pointer to the action format of the device that has the given GUID
|
|
HRESULT CConfigWnd::GetActionFormatFromInstanceGuid(LPDIACTIONFORMATW *lplpAcFor, REFGUID Guid)
|
|
{
|
|
if (!lplpAcFor)
|
|
return E_INVALIDARG;
|
|
|
|
for (int i = 0; i < GetNumElements(); i++)
|
|
{
|
|
ELEMENT &e = m_Element[i];
|
|
|
|
if (e.didi.guidInstance == Guid)
|
|
{
|
|
*lplpAcFor = GetCurAcFor(e);
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HDC CConfigWnd::GetRenderDC()
|
|
{
|
|
assert(InRenderMode());
|
|
|
|
if (m_bRender3D)
|
|
return m_pbm3D == NULL ? NULL : m_pbm3D->BeginPaintInto();
|
|
else
|
|
{
|
|
if (m_pSurface == NULL)
|
|
return NULL;
|
|
|
|
HDC hDC = NULL;
|
|
HRESULT hr = m_pSurface->GetDC(&hDC);
|
|
if (FAILED(hr))
|
|
if (hr == DDERR_SURFACELOST)
|
|
{
|
|
m_pSurface->Restore(); // Restore the surface
|
|
hr = m_pSurface->GetDC(&hDC); // Retry
|
|
if (FAILED(hr))
|
|
return NULL;
|
|
}
|
|
else
|
|
return NULL;
|
|
|
|
return hDC;
|
|
}
|
|
}
|
|
|
|
void CConfigWnd::ReleaseRenderDC(HDC &phDC)
|
|
{
|
|
assert(InRenderMode());
|
|
|
|
HDC hDC = phDC;
|
|
phDC = NULL;
|
|
|
|
if (m_bRender3D)
|
|
{
|
|
if (m_pbm3D == NULL)
|
|
return;
|
|
|
|
m_pbm3D->EndPaintInto(hDC);
|
|
}
|
|
else
|
|
{
|
|
if (m_pSurface == NULL)
|
|
return;
|
|
|
|
m_pSurface->ReleaseDC(hDC);
|
|
}
|
|
}
|
|
|
|
struct BITMAPINFO_3MASKS
|
|
{
|
|
BITMAPINFOHEADER bmiHeader;
|
|
RGBQUAD bmiColors[3];
|
|
};
|
|
|
|
void CConfigWnd::Create3DBitmap()
|
|
{
|
|
HDC hDC = CreateCompatibleDC(NULL);
|
|
|
|
BITMAPINFO_3MASKS bmi3mask; // BITMAPINFO with 3 DWORDs for bmiColors
|
|
BITMAPINFO *pbmi = (BITMAPINFO*)&bmi3mask;
|
|
|
|
BITMAPINFOHEADER &h = pbmi->bmiHeader;
|
|
h.biSize = sizeof(BITMAPINFOHEADER);
|
|
h.biWidth = WINDOW_WIDTH;
|
|
h.biHeight = -WINDOW_HEIGHT;
|
|
h.biPlanes = 1;
|
|
h.biSizeImage = 0;
|
|
h.biXPelsPerMeter = 100;
|
|
h.biYPelsPerMeter = 100;
|
|
h.biClrImportant = 0;
|
|
|
|
// Get the surface's pixel format
|
|
D3DSURFACE_DESC d3dsd;
|
|
ZeroMemory(&d3dsd, sizeof(d3dsd));
|
|
m_pSurface3D->GetDesc(&d3dsd);
|
|
m_SurfFormat = d3dsd.Format;
|
|
switch(d3dsd.Format)
|
|
{
|
|
case D3DFMT_R5G6B5:
|
|
h.biClrUsed = 3;
|
|
h.biBitCount = 16;
|
|
m_uiPixelSize = 2;
|
|
h.biCompression = BI_BITFIELDS;
|
|
*((LPDWORD)pbmi->bmiColors) = 0xF800;
|
|
*((LPDWORD)pbmi->bmiColors+1) = 0x07E0;
|
|
*((LPDWORD)pbmi->bmiColors+2) = 0x001F;
|
|
break;
|
|
|
|
case D3DFMT_X1R5G5B5:
|
|
case D3DFMT_A1R5G5B5:
|
|
h.biClrUsed = 3;
|
|
h.biBitCount = 16;
|
|
m_uiPixelSize = 2;
|
|
h.biCompression = BI_BITFIELDS;
|
|
*((LPDWORD)pbmi->bmiColors) = 0x7C00;
|
|
*((LPDWORD)pbmi->bmiColors+1) = 0x03E0;
|
|
*((LPDWORD)pbmi->bmiColors+2) = 0x001F;
|
|
break;
|
|
|
|
case D3DFMT_R8G8B8:
|
|
h.biClrUsed = 0;
|
|
h.biBitCount = 24;
|
|
m_uiPixelSize = 3;
|
|
h.biCompression = BI_RGB;
|
|
break;
|
|
|
|
case D3DFMT_A8R8G8B8:
|
|
case D3DFMT_X8R8G8B8:
|
|
default:
|
|
// Use 32 bits for all other formats
|
|
h.biClrUsed = 0;
|
|
h.biBitCount = 32;
|
|
m_uiPixelSize = 4;
|
|
h.biCompression = BI_RGB;
|
|
break;
|
|
}
|
|
|
|
HBITMAP hbm = CreateDIBSection(
|
|
hDC,
|
|
pbmi,
|
|
DIB_RGB_COLORS,
|
|
&m_p3DBits,
|
|
NULL,
|
|
0);
|
|
|
|
DeleteDC(hDC);
|
|
hDC = NULL;
|
|
|
|
if (hbm != NULL)
|
|
m_pbm3D = CBitmap::StealToCreate(hbm);
|
|
|
|
if (hbm != NULL)
|
|
DeleteObject((HGDIOBJ)hbm);
|
|
hbm = NULL;
|
|
}
|
|
|
|
void CConfigWnd::Copy3DBitmapToSurface3D()
|
|
{
|
|
assert(m_bRender3D);
|
|
|
|
if (m_p3DBits == NULL || m_pbm3D == NULL || m_pSurface3D == NULL)
|
|
{
|
|
etrace(_T("One or more of the vars required for Copy3DBitmapToSurface() was NULL!\n"));
|
|
return;
|
|
}
|
|
|
|
RECT rect = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
|
|
|
|
HRESULT hr = D3DXLoadSurfaceFromMemory(
|
|
m_pSurface3D,
|
|
NULL,
|
|
NULL,//&rect,
|
|
m_p3DBits,
|
|
m_SurfFormat,
|
|
WINDOW_WIDTH * m_uiPixelSize,
|
|
NULL,
|
|
&rect,
|
|
D3DX_FILTER_POINT,
|
|
0); // Disable Color Key
|
|
}
|
|
|
|
void CConfigWnd::CallRenderCallback()
|
|
{
|
|
LPDICONFIGUREDEVICESCALLBACK pCallback = m_uig.GetCallback();
|
|
LPVOID pvRefData = m_uig.GetRefData();
|
|
|
|
if (pCallback == NULL)
|
|
return;
|
|
|
|
if (m_bRender3D)
|
|
{
|
|
Copy3DBitmapToSurface3D();
|
|
pCallback(m_pSurface3D, pvRefData);
|
|
}
|
|
else
|
|
{
|
|
pCallback(m_pSurface, pvRefData);
|
|
}
|
|
}
|
|
|
|
void CConfigWnd::OnRender(BOOL bInternalCall)
|
|
{
|
|
m_bNeedRedraw = TRUE;
|
|
}
|
|
|
|
void CConfigWnd::Render(BOOL bInternalCall)
|
|
{
|
|
tracescope(__ts, _T("CConfigWnd::Render() "));
|
|
traceBOOL(bInternalCall);
|
|
|
|
m_bNeedRedraw = FALSE;
|
|
|
|
ValidateRect(m_hWnd, NULL);
|
|
|
|
if (m_hWnd == NULL)
|
|
return;
|
|
|
|
HDC hDC = GetRenderDC();
|
|
if (hDC == NULL)
|
|
return;
|
|
|
|
if (bInternalCall)
|
|
RenderInto(hDC);
|
|
|
|
static ICONINFO IconInfo;
|
|
static HCURSOR hOldCursor = NULL;
|
|
static HCURSOR hCursor;
|
|
if (m_bHourGlass)
|
|
hCursor = LoadCursor(NULL, IDC_WAIT);
|
|
else
|
|
hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
if (hCursor == NULL)
|
|
return;
|
|
|
|
if (hOldCursor != hCursor)
|
|
{
|
|
hOldCursor = hCursor;
|
|
GetIconInfo(hCursor, &IconInfo);
|
|
|
|
if (IconInfo.hbmMask)
|
|
DeleteObject(IconInfo.hbmMask);
|
|
|
|
if (IconInfo.hbmColor)
|
|
DeleteObject(IconInfo.hbmColor);
|
|
}
|
|
|
|
POINT pt;
|
|
GetCursorPos(&pt);
|
|
|
|
ScreenToClient(m_hWnd, &pt);
|
|
|
|
pt.x -= IconInfo.xHotspot;
|
|
pt.y -= IconInfo.yHotspot;
|
|
|
|
if (m_pbmPointerEraser)
|
|
m_pbmPointerEraser->Get(hDC, pt);
|
|
|
|
// If m_bHourGlass is true, we are resetting, so we don't draw mouse cursor.
|
|
if (hCursor && !m_bHourGlass)
|
|
DrawIcon(hDC, pt.x, pt.y, hCursor);
|
|
|
|
ReleaseRenderDC(hDC);
|
|
|
|
CallRenderCallback();
|
|
|
|
hDC = GetRenderDC();
|
|
if (hDC == NULL)
|
|
return;
|
|
|
|
if (m_pbmPointerEraser)
|
|
m_pbmPointerEraser->Draw(hDC, pt);
|
|
|
|
ReleaseRenderDC(hDC);
|
|
}
|
|
|
|
void CConfigWnd::Unacquire()
|
|
{
|
|
for (int i = 0; i < GetNumElements(); i++)
|
|
{
|
|
ELEMENT &e = m_Element[i];
|
|
if (e.pPage != NULL)
|
|
e.pPage->Unacquire();
|
|
}
|
|
}
|
|
|
|
void CConfigWnd::Reacquire()
|
|
{
|
|
for (int i = 0; i < GetNumElements(); i++)
|
|
{
|
|
ELEMENT &e = m_Element[i];
|
|
if (e.pPage != NULL)
|
|
e.pPage->Reacquire();
|
|
}
|
|
}
|
|
|
|
HRESULT CConfigWnd::Reset()
|
|
{
|
|
RECT rect;
|
|
int iCenterX, iCenterY;
|
|
|
|
GetClientRect(&rect);
|
|
iCenterX = (rect.left + rect.right) >> 1;
|
|
iCenterY = (rect.top + rect.bottom) >> 1;
|
|
rect.left = rect.right = iCenterX;
|
|
rect.top = rect.bottom = iCenterY;
|
|
InflateRect(&rect, g_iResetMsgBoxWidth >> 1, g_iResetMsgBoxHeight >> 1);
|
|
|
|
m_MsgBox.Create(m_hWnd, rect, FALSE);
|
|
m_MsgBox.SetNotify(m_hWnd);
|
|
m_MsgBox.SetFont((HFONT)m_uig.GetFont(UIE_USERNAMES));
|
|
m_MsgBox.SetColors(m_uig.GetTextColor(UIE_USERNAMES),
|
|
m_uig.GetBkColor(UIE_USERNAMES),
|
|
m_uig.GetTextColor(UIE_USERNAMESEL),
|
|
m_uig.GetBkColor(UIE_USERNAMESEL),
|
|
m_uig.GetBrushColor(UIE_USERNAMES),
|
|
m_uig.GetPenColor(UIE_USERNAMES));
|
|
|
|
TCHAR tszResourceString[MAX_PATH];
|
|
LoadString(g_hModule, IDS_RESETMSG, tszResourceString, MAX_PATH);
|
|
m_MsgBox.SetText(tszResourceString);
|
|
::ShowWindow(m_MsgBox.m_hWnd, SW_SHOW);
|
|
::SetWindowPos(m_MsgBox.m_hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
|
|
m_MsgBox.Invalidate();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CConfigWnd::QueryActionAssignedAnywhere(GUID GuidInstance, int i)
|
|
{
|
|
return IsActionAssignedAnywhere(GuidInstance, i) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
int CConfigWnd::GetNumGenres()
|
|
{
|
|
return m_uig.GetNumMasterAcFors();
|
|
}
|
|
|
|
HRESULT CConfigWnd::SetCurUser(int nPage, int nUser)
|
|
{
|
|
// make sure we're using a valid element index
|
|
if (nPage < 0 || nPage >= GetNumElements())
|
|
{
|
|
assert(0);
|
|
return E_FAIL;
|
|
}
|
|
|
|
// get the element
|
|
ELEMENT &e = GetElement(nPage);
|
|
|
|
// don't do anything if we're already set to this user
|
|
if (e.nCurUser == nUser)
|
|
return S_OK;
|
|
|
|
// store new curuser
|
|
e.nCurUser = nUser;
|
|
|
|
// if this page isn't the one currently shown, do nothing
|
|
// (it'll get the new acfor when it's shown)
|
|
if (m_CurSel != nPage)
|
|
return S_OK;
|
|
|
|
// otherwised, cycle the page to reflect change
|
|
if (e.pPage)
|
|
e.pPage->Unacquire();
|
|
HidePage(nPage);
|
|
ShowPage(nPage);
|
|
if (e.pPage)
|
|
e.pPage->Reacquire();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CConfigWnd::SetCurGenre(int NewGenre)
|
|
{
|
|
// if no change, do nothing
|
|
if (NewGenre == m_nCurGenre)
|
|
return S_OK;
|
|
|
|
// make sure genre index is in range
|
|
if (NewGenre < 0 || NewGenre >= GetNumGenres())
|
|
return E_INVALIDARG;
|
|
|
|
// set genre
|
|
m_nCurGenre = NewGenre;
|
|
|
|
// store which page is currently up
|
|
int iOldPage = m_CurSel;
|
|
|
|
// for each page...
|
|
BOOL bShown = FALSE;
|
|
for (int i = 0; i < GetNumElements(); i++)
|
|
{
|
|
ELEMENT &e = GetElement(i);
|
|
|
|
// hide the page and unacquire its device
|
|
if (e.pPage)
|
|
{
|
|
e.pPage->Unacquire();
|
|
HidePage(i);
|
|
}
|
|
|
|
// show page if it was the old cur page
|
|
if (i == iOldPage && e.pPage && GetCurAcFor(e))
|
|
{
|
|
ShowPage(i);
|
|
bShown = TRUE;
|
|
}
|
|
|
|
// reacquire device
|
|
if (e.pPage)
|
|
e.pPage->Reacquire();
|
|
}
|
|
|
|
// if nothing was shown, show something
|
|
if (!bShown && GetNumElements() > 0)
|
|
{
|
|
m_CurSel = -1;
|
|
SelTab(0);
|
|
}
|
|
|
|
// if we showed the one we expected to show, we succeeded
|
|
return bShown ? S_OK : E_FAIL;
|
|
}
|
|
|
|
int CConfigWnd::GetCurGenre()
|
|
{
|
|
return m_nCurGenre;
|
|
}
|
|
|
|
HWND CConfigWnd::GetMainHWND()
|
|
{
|
|
return m_hWnd;
|
|
}
|
|
|
|
// This is called by CDIDeviceActionConfigPage::DeviceUINotify.
|
|
// We scan the ELEMENT array and when we find a match, destroy and recreate the device
|
|
// object, then return it back to CDIDeviceActionConfigPage so it can update its pointer.
|
|
LPDIRECTINPUTDEVICE8W CConfigWnd::RenewDevice(GUID &GuidInstance)
|
|
{
|
|
for (int i = 0; i < GetNumElements(); i++)
|
|
{
|
|
ELEMENT &e = GetElement(i);
|
|
|
|
if (e.didi.guidInstance == GuidInstance)
|
|
{
|
|
// Releaes the instance we have
|
|
if (e.lpDID)
|
|
{
|
|
e.lpDID->Release();
|
|
e.lpDID = NULL;
|
|
}
|
|
// Recreate the device
|
|
e.lpDID = CreateDevice(e.didi.guidInstance);
|
|
return e.lpDID;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
LPDIACTIONFORMATW ELEMENT::GetAcFor(int nGenre, int nUser, BOOL bHwDefault)
|
|
{
|
|
// return null if requesting for unassigned user
|
|
if (nUser == -1)
|
|
return NULL;
|
|
|
|
// validate params
|
|
if (!lpDID || !pUIGlobals || nGenre < 0 ||
|
|
nGenre >= pUIGlobals->GetNumMasterAcFors() ||
|
|
nUser < 0 || nUser >= pUIGlobals->GetNumUserNames())
|
|
{
|
|
etrace(_T("ELEMENT::GetAcFor(): Invalid params\n"));
|
|
return NULL;
|
|
}
|
|
|
|
// generate dword id for map entry
|
|
DWORD dwMap = GENREUSER2MAP(nGenre, nUser);
|
|
|
|
// try to get that acfor
|
|
LPDIACTIONFORMATW lpAcFor = NULL;
|
|
BOOL bFound = AcForMap.Lookup(dwMap, lpAcFor);
|
|
|
|
// if we found it and its not null and we are asked for hardware default setting, return it
|
|
if (bFound && lpAcFor && !bHwDefault)
|
|
return lpAcFor;
|
|
|
|
// otherwise... we gotta make it
|
|
tracescope(__ts, _T("ELEMENT::GetAcFor"));
|
|
trace2(_T("(%d, %d)\n"), nGenre, nUser);
|
|
trace1(_T("Building map entry 0x%08x...\n"), dwMap);
|
|
|
|
// copy it from the masteracfor for the genre
|
|
lpAcFor = DupActionFormat(&(pUIGlobals->RefMasterAcFor(nGenre)));
|
|
if (!lpAcFor)
|
|
{
|
|
etrace(_T("DupActionFormat() failed\n"));
|
|
return NULL;
|
|
}
|
|
|
|
// build it for the user
|
|
DWORD dwFlags = 0;
|
|
if (bHwDefault
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
|| pUIGlobals->QueryAllowEditLayout()
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
)
|
|
dwFlags |= DIDBAM_HWDEFAULTS;
|
|
LPCWSTR wszUserName = pUIGlobals->GetUserName(nUser);
|
|
HRESULT hr = lpDID->BuildActionMap(lpAcFor, wszUserName, dwFlags);
|
|
if (FAILED(hr))
|
|
{
|
|
etrace4(_T("BuildActionMap(0x%p, %s, 0x%08x) failed, returning 0x%08x\n"), lpAcFor, QSAFESTR(wszUserName), dwFlags, hr);
|
|
FreeActionFormatDup(lpAcFor);
|
|
lpAcFor = NULL;
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
trace3(_T("BuildActionMap(0x%p, %s, 0x%08x) succeeded\n"), lpAcFor, QSAFESTR(wszUserName), dwFlags);
|
|
// Now we check if the return code is DI_WRITEPROTECT. If so, device can't be remapped.
|
|
if (hr == DI_WRITEPROTECT)
|
|
{
|
|
// The way we disable mapping is to add DIA_APPFIXED flag to all actions.
|
|
for (DWORD i = 0; i < lpAcFor->dwNumActions; ++i)
|
|
lpAcFor->rgoAction[i].dwFlags |= DIA_APPFIXED;
|
|
}
|
|
}
|
|
|
|
// Here we copy the DIA_APPFIXED flag back to our action format for all DIACTION that had this.
|
|
const DIACTIONFORMATW &MasterAcFor = pUIGlobals->RefMasterAcFor(nGenre);
|
|
for (DWORD i = 0; i < MasterAcFor.dwNumActions; ++i)
|
|
if (MasterAcFor.rgoAction[i].dwFlags & DIA_APPFIXED)
|
|
lpAcFor->rgoAction[i].dwFlags |= DIA_APPFIXED;
|
|
// set it in the map
|
|
assert(lpAcFor != NULL);
|
|
AcForMap.SetAt(dwMap, lpAcFor);
|
|
|
|
// return it
|
|
return lpAcFor;
|
|
}
|
|
|
|
void ELEMENT::FreeMap()
|
|
{
|
|
POSITION pos = AcForMap.GetStartPosition();
|
|
while (pos != NULL)
|
|
{
|
|
DWORD dwMap = (DWORD)-1;
|
|
LPDIACTIONFORMATW lpAcFor = NULL;
|
|
AcForMap.GetNextAssoc(pos, dwMap, lpAcFor);
|
|
|
|
if (lpAcFor)
|
|
FreeActionFormatDup(lpAcFor);
|
|
}
|
|
AcForMap.RemoveAll();
|
|
}
|
|
|
|
void ELEMENT::Apply()
|
|
{
|
|
tracescope(tsa, _T("\nELEMENT::Apply()\n"));
|
|
trace1(_T("Applying Changes to Device %s\n"), QSAFESTR(didi.tszInstanceName));
|
|
|
|
if (lpDID == NULL)
|
|
{
|
|
etrace(_T("NULL lpDID, can't apply\n"));
|
|
return;
|
|
}
|
|
|
|
if (pUIGlobals == NULL)
|
|
{
|
|
etrace(_T("NULL pUIGlobals, can't apply\n"));
|
|
return;
|
|
}
|
|
|
|
LPDIACTIONFORMATW lpAcFor = NULL;
|
|
|
|
// go through the map and add the map keys to last if the user
|
|
// is the current user assignment, or to first if not
|
|
CList<DWORD, DWORD &> first, last;
|
|
POSITION pos = AcForMap.GetStartPosition();
|
|
while (pos != NULL)
|
|
{
|
|
DWORD dwMap = (DWORD)-1;
|
|
lpAcFor = NULL;
|
|
AcForMap.GetNextAssoc(pos, dwMap, lpAcFor);
|
|
|
|
if (MAP2USER(dwMap) == nCurUser)
|
|
last.AddTail(dwMap);
|
|
else
|
|
first.AddTail(dwMap);
|
|
}
|
|
|
|
// concatenate lists
|
|
first.AddTail(&last);
|
|
|
|
// now go through the resulting list (so that the current
|
|
// assignments are set last) if this device is assigned.
|
|
if (nCurUser != -1)
|
|
{
|
|
pos = first.GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
DWORD dwMap = first.GetNext(pos);
|
|
lpAcFor = AcForMap[dwMap];
|
|
|
|
tracescope(tsa2, _T("Applying lpAcFor at AcForMap["));
|
|
trace1(_T("0x%08x]...\n"), dwMap);
|
|
|
|
if (lpAcFor == NULL)
|
|
{
|
|
etrace(_T("NULL lpAcFor, can't apply\n"));
|
|
continue;
|
|
}
|
|
|
|
int nGenre = MAP2GENRE(dwMap);
|
|
int nUser = MAP2USER(dwMap);
|
|
LPCWSTR wszUserName = pUIGlobals->GetUserName(nUser);
|
|
|
|
traceLONG(nGenre);
|
|
traceLONG(nUser);
|
|
traceWSTR(wszUserName);
|
|
|
|
TraceActionFormat(_T("Final Device ActionFormat:"), *lpAcFor);
|
|
|
|
for (DWORD j = 0; j < lpAcFor->dwNumActions; ++j)
|
|
{
|
|
if( lpAcFor->rgoAction[j].dwObjID == (DWORD)-1 || IsEqualGUID(lpAcFor->rgoAction[j].guidInstance, GUID_NULL))
|
|
{
|
|
lpAcFor->rgoAction[j].dwHow = DIAH_UNMAPPED;
|
|
}
|
|
else if( lpAcFor->rgoAction[j].dwHow &
|
|
( DIAH_USERCONFIG | DIAH_APPREQUESTED | DIAH_HWAPP | DIAH_HWDEFAULT | DIAH_DEFAULT ) )
|
|
{
|
|
//@@BEGIN_MSINTERNAL
|
|
// ISSUE-2001/03/27-MarcAnd should look at doing this less destructively
|
|
// that is if everything is defaults then leave them alon
|
|
//@@END_MSINTERNAL
|
|
lpAcFor->rgoAction[j].dwHow = DIAH_USERCONFIG;
|
|
}
|
|
else if(IsEqualGUID(didi.guidInstance,lpAcFor->rgoAction[j].guidInstance))
|
|
{
|
|
lpAcFor->rgoAction[j].dwHow = DIAH_USERCONFIG;
|
|
}
|
|
}
|
|
|
|
HRESULT hr;
|
|
hr = lpDID->SetActionMap(lpAcFor, wszUserName, DIDSAM_FORCESAVE|DIDSAM_DEFAULT);
|
|
|
|
if (FAILED(hr))
|
|
etrace1(_T("SetActionMap() failed, returning 0x%08x\n"), hr);
|
|
else
|
|
trace(_T("SetActionMap() succeeded\n"));
|
|
}
|
|
} // if (nCurUser != -1)
|
|
else
|
|
{
|
|
// we're unassigned, set null
|
|
trace(_T("Unassigning...\n"));
|
|
|
|
// we need an acfor to unassign, so get one if don't have
|
|
// one left over from what we just did
|
|
if (!lpAcFor)
|
|
lpAcFor = GetAcFor(0, 0);
|
|
|
|
if (!lpAcFor)
|
|
etrace(_T("Couldn't get an acfor for unassignment\n"));
|
|
|
|
HRESULT hr;
|
|
hr = lpDID->SetActionMap(lpAcFor, NULL, DIDSAM_NOUSER);
|
|
|
|
if (FAILED(hr))
|
|
etrace1(_T("SetActionMap() failed, returning 0x%08x\n"), hr);
|
|
else
|
|
trace(_T("SetActionMap() succeeded\n"));
|
|
}
|
|
}
|
|
|
|
int CConfigWnd::GetNumUsers()
|
|
{
|
|
return m_uig.GetNumUserNames();
|
|
}
|
|
|
|
int CConfigWnd::GetCurUser(int nPage)
|
|
{
|
|
return GetElement(nPage).nCurUser;
|
|
}
|