|
|
//-----------------------------------------------------------------------------
// File: cdiacpage.cpp
//
// Desc: CDIDeviceActionConfigPage implements the page object used by the UI.
// A page covers the entire UI minus the device tabs and the bottons at
// the bottom. The information window, player combo-box, genre combo-
// box, action list tree, and device view window are all managed by
// the page.
//
// Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
//-----------------------------------------------------------------------------
#include "common.hpp"
#include <initguid.h>
DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
// {D0B5C9AE-966F-4510-B955-4D2482C5EB1B}
DEFINE_GUID(GUID_ActionItem, 0xd0b5c9ae, 0x966f, 0x4510, 0xb9, 0x55, 0x4d, 0x24, 0x82, 0xc5, 0xeb, 0x1b);
#define DISEM_TYPE_MASK ( 0x00000600 )
#define DISEM_REL_MASK ( 0x00000100 )
#define DISEM_REL_SHIFT ( 8 )
#define DISEM_TYPE_AXIS 0x00000200
#define DISEM_TYPE_BUTTON 0x00000400
#define DISEM_TYPE_POV 0x00000600
#define DEVICE_POLLING_INTERVAL 10
#define DEVICE_POLLING_AXIS_MIN 0
#define DEVICE_POLLING_AXIS_MAX 100
#define DEVICE_POLLING_AXIS_MINDELTA 3
#define DEVICE_POLLING_AXIS_SIGNIFICANT 40
#define DEVICE_POLLING_AXIS_ACCUMULATION 20
#define DEVICE_POLLING_ACBUF_START_INDEX 3
#define DEVICE_POLLING_WHEEL_SCALE_FACTOR 3
// For WINMM.DLL
HINSTANCE g_hWinMmDLL = NULL; FUNCTYPE_timeSetEvent g_fptimeSetEvent = NULL;
//QueryInterface
STDMETHODIMP CDIDeviceActionConfigPage::QueryInterface(REFIID iid, LPVOID* ppv) { //null the out param
*ppv = NULL;
if ((iid == IID_IUnknown) || (iid == IID_IDIDeviceActionConfigPage)) { *ppv = this; AddRef(); return S_OK; }
return E_NOINTERFACE; }
//AddRef
STDMETHODIMP_(ULONG) CDIDeviceActionConfigPage::AddRef() { return InterlockedIncrement(&m_cRef); }
//Release
STDMETHODIMP_(ULONG) CDIDeviceActionConfigPage::Release() {
if (InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; }
return m_cRef; }
//constructor
CDIDeviceActionConfigPage::CDIDeviceActionConfigPage() : m_pDeviceUI(NULL), m_puig(NULL), m_pUIFrame(NULL), m_cRef(1), m_lpDiac(NULL), m_State(CFGSTATE_NORMAL), m_pCurControl(NULL), m_tszIBText(NULL), m_pbmIB(NULL), m_pbmIB2(NULL), m_pbmRelAxesGlyph(NULL), m_pbmAbsAxesGlyph(NULL), m_pbmButtonGlyph(NULL), m_pbmHatGlyph(NULL), m_pbmCheckGlyph(NULL), m_pbmCheckGlyphDark(NULL), m_pRelAxesParent(NULL), m_pAbsAxesParent(NULL), m_pButtonParent(NULL), m_pHatParent(NULL), m_pUnknownParent(NULL), m_bFirstDeviceData(TRUE), m_cbDeviceDataSize(0), m_nOnDeviceData(0), m_dwLastControlType(0), m_nPageIndex(-1) { tracescope(__ts, _T("CDIDeviceActionConfigPage::CDIDeviceActionConfigPage()\n")); m_pDeviceData[0] = NULL; m_pDeviceData[1] = NULL; }
//destructor
CDIDeviceActionConfigPage::~CDIDeviceActionConfigPage() { tracescope(__ts, _T("CDIDeviceActionConfigPage::~CDIDeviceActionConfigPage()\n"));
// Unattach the parent from the tooltip window so it won't get destroyed.
SetParent(CFlexWnd::s_ToolTip.m_hWnd, NULL);
if (m_hWnd != NULL) Destroy();
FreeResources();
delete m_pDeviceUI;
for (int c = 0; c < 2; c++) if (m_pDeviceData[c] != NULL) free(m_pDeviceData[c]);
if (m_lpDID != NULL) { m_lpDID->Unacquire(); m_lpDID->Release(); } m_lpDID = NULL; }
STDMETHODIMP CDIDeviceActionConfigPage::Create(DICFGPAGECREATESTRUCT *pcs) { tracescope(__ts, _T("CDIDeviceActionConfigPage::Create()\n")); if (pcs == NULL) return E_INVALIDARG; DICFGPAGECREATESTRUCT &cs = *pcs; // validate/save uig and uif
m_puig = pcs->pUIGlobals; m_pUIFrame = pcs->pUIFrame; if (m_puig == NULL || m_pUIFrame == NULL) return E_INVALIDARG;
// save page index
m_nPageIndex = pcs->nPage; assert(m_nPageIndex >= 0);
// create deviceui with uig, or fail
m_pDeviceUI = new CDeviceUI(*m_puig, *m_pUIFrame); if (m_pDeviceUI == NULL) return E_FAIL;
// save the device instance
m_didi = cs.didi; m_lpDID = cs.lpDID; if (m_lpDID != NULL) m_lpDID->AddRef();
// create the window
HWND hWnd = NULL; assert(m_puig != NULL); //@@BEGIN_MSINTERNAL
#ifdef DDKBUILD
BOOL bAllowEditLayout = m_puig->QueryAllowEditLayout(); #endif
//@@END_MSINTERNAL
RECT rect = {0, 0, 1, 1}; hWnd = CFlexWnd::Create(cs.hParentWnd, rect, FALSE);
// return the handle
cs.hPageWnd = hWnd;
assert(m_puig != NULL);
// Create the information box
m_InfoBox.Create(m_hWnd, g_InfoWndRect, TRUE); m_InfoBox.SetFont((HFONT)m_puig->GetFont(UIE_USERNAMES)); m_InfoBox.SetColors(m_puig->GetTextColor(UIE_USERNAMES), m_puig->GetBkColor(UIE_USERNAMES), m_puig->GetTextColor(UIE_USERNAMESEL), m_puig->GetBkColor(UIE_USERNAMESEL), m_puig->GetBrushColor(UIE_USERNAMES), m_puig->GetPenColor(UIE_USERNAMES)); SetAppropriateDefaultText();
// Create the check box only if this is a keyboard device.
if (LOBYTE(LOWORD(m_didi.dwDevType)) == DI8DEVTYPE_KEYBOARD) { m_CheckBox.Create(m_hWnd, g_CheckBoxRect, FALSE); m_CheckBox.SetNotify(m_hWnd); m_CheckBox.SetFont((HFONT)m_puig->GetFont(UIE_USERNAMES)); m_CheckBox.SetColors(m_puig->GetTextColor(UIE_USERNAMES), m_puig->GetBkColor(UIE_USERNAMES), m_puig->GetTextColor(UIE_USERNAMESEL), m_puig->GetBkColor(UIE_USERNAMESEL), m_puig->GetBrushColor(UIE_USERNAMES), m_puig->GetPenColor(UIE_USERNAMES));
TCHAR tszResourceString[MAX_PATH]; LoadString(g_hModule, IDS_SORTASSIGNED, tszResourceString, MAX_PATH); m_CheckBox.SetText(tszResourceString); m_CheckBox.SetCheck(TRUE); ::ShowWindow(m_CheckBox.m_hWnd, SW_SHOW); }
// create the username dropdown if necessary
FLEXCOMBOBOXCREATESTRUCT cbcs; cbcs.dwSize = sizeof(FLEXCOMBOBOXCREATESTRUCT); cbcs.dwFlags = FCBF_DEFAULT; cbcs.dwListBoxFlags = FCBF_DEFAULT|FLBF_INTEGRALHEIGHT; cbcs.hWndParent = m_hWnd; cbcs.hWndNotify = m_hWnd; cbcs.bVisible = TRUE; cbcs.rect = g_UserNamesRect; cbcs.hFont = (HFONT)m_puig->GetFont(UIE_USERNAMES); cbcs.rgbText = m_puig->GetTextColor(UIE_USERNAMES); cbcs.rgbBk = m_puig->GetBkColor(UIE_USERNAMES); cbcs.rgbSelText = m_puig->GetTextColor(UIE_USERNAMESEL); cbcs.rgbSelBk = m_puig->GetBkColor(UIE_USERNAMESEL); cbcs.rgbFill = m_puig->GetBrushColor(UIE_USERNAMES); cbcs.rgbLine = m_puig->GetPenColor(UIE_USERNAMES); cbcs.nSBWidth = 11;
if (m_puig->GetNumUserNames() > 0 && m_hWnd != NULL //@@BEGIN_MSINTERNAL
#ifdef DDKBUILD
&& !m_puig->QueryAllowEditLayout() #endif
//@@END_MSINTERNAL
) { for (int i = 0, n = m_puig->GetNumUserNames(); i < n; i++) m_UserNames.AddString(SAFESTR(m_puig->GetUserName(i))); m_UserNames.AddString(SAFESTR(_T("(unassigned)")));
m_UserNames.Create(&cbcs);
int nUser = m_pUIFrame->GetCurUser(m_nPageIndex); if (nUser == -1) nUser = m_puig->GetNumUserNames(); m_UserNames.SetSel(nUser); } else if (m_hWnd != NULL) m_UserNames.SetSel(0); // If only 1 user, still must set selection to 0 or we get error later.
// If we are in view mode, set username combobox to read only so user can't change its value.
if (!m_puig->InEditMode()) m_UserNames.SetReadOnly(TRUE);
if (m_puig->GetNumMasterAcFors() > 1 && m_hWnd != NULL) { for (int i = 0, n = m_puig->GetNumMasterAcFors(); i < n; i++) m_Genres.AddString(SAFESTR(m_puig->RefMasterAcFor(i).tszActionMap));
cbcs.rect = g_GenresRect; m_Genres.Create(&cbcs); m_Genres.SetSel(m_pUIFrame->GetCurGenre()); }
// return success/fail
return hWnd != NULL ? S_OK : E_FAIL; }
STDMETHODIMP CDIDeviceActionConfigPage::Show(LPDIACTIONFORMATW lpDiActFor) { // save the format pointer
m_lpDiac = lpDiActFor;
// force tree init
InitTree(TRUE);
// show the assignments for the controls
SetControlAssignments();
// show the assignment for the current control
ShowCurrentControlAssignment();
// Sort the list if check box is checked.
if (m_CheckBox.GetCheck()) m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
// show the window
if (m_hWnd != NULL) ShowWindow(m_hWnd, SW_SHOW);
SetFocus(m_hWnd); CFlexWnd::s_CurrPageHwnd = m_hWnd;
return S_OK; }
STDMETHODIMP CDIDeviceActionConfigPage::Hide() { // clear the tree
ClearTree();
// null the format pointer
m_lpDiac = NULL;
// hide the window
if (m_hWnd != NULL) ShowWindow(m_hWnd, SW_HIDE);
// If we are in the assign state, exit it.
if (m_State == CFGSTATE_ASSIGN) ExitAssignState();
return S_OK; }
void CDIDeviceActionConfigPage::InitIB() { RECT z = {0,0,0,0}; SIZE bsize = {0,0}; m_rectIB = z; if (m_pbmIB != NULL) { if (m_pbmIB->GetSize(&bsize)) { m_rectIB.right = bsize.cx * 2; m_rectIB.bottom = bsize.cy; } }
const int IBORIGINX = 200, IBORIGINY = 394, IBTEXTMARGINLEFT = 5; POINT ptIBOrigin = {IBORIGINX, IBORIGINY};
m_tszIBText = _T("Click here to see different views of your controller."); SIZE tsize = GetTextSize(m_tszIBText, (HFONT)m_puig->GetFont(UIE_VIEWSEL)); m_ptIBOffset.x = 0; m_ptIBOffset.y = 0; int tofs = 0; if (m_rectIB.bottom < tsize.cy) { m_rectIB.bottom = tsize.cy; m_ptIBOffset.y = (tsize.cy - bsize.cy) / 2; } else if (tsize.cy < m_rectIB.bottom) tofs = (bsize.cy - tsize.cy) / 2; m_rectIB.right += tsize.cx; if (m_pbmIB != NULL) m_rectIB.right += IBTEXTMARGINLEFT * 2;
OffsetRect(&m_rectIB, ptIBOrigin.x, ptIBOrigin.y); m_ptIBOffset.x += ptIBOrigin.x; m_ptIBOffset.y += ptIBOrigin.y;
m_ptIBOffset2.x = m_rectIB.right - bsize.cx; m_ptIBOffset2.y = m_ptIBOffset.y;
m_rectIBText = m_rectIB; if (m_pbmIB != NULL) m_rectIBText.left += IBTEXTMARGINLEFT + bsize.cx; if (m_pbmIB2 != NULL) m_rectIBText.right -= IBTEXTMARGINLEFT + bsize.cx; m_rectIBText.top += tofs;
// Inialize the two RECTs representing the two arrow bitmaps
m_rectIBLeft = m_rectIBRight = m_rectIB; m_rectIBLeft.right = m_rectIBText.left; m_rectIBRight.left = m_rectIBText.right; }
void CDIDeviceActionConfigPage::OnInit() { tracescope(__ts, _T("CDIDeviceActionConfigPage::OnInit()\n")); // init resources
InitResources();
// init IB
InitIB();
// initialize the device UI
m_pDeviceUI->Init(m_didi, m_lpDID, m_hWnd, this);
// initialize the device
InitDevice();
// Start a one-shot timer for click to pick
if (g_fptimeSetEvent) g_fptimeSetEvent(DEVICE_POLLING_INTERVAL, DEVICE_POLLING_INTERVAL, CDIDeviceActionConfigPage::DeviceTimerProc, (DWORD_PTR)m_hWnd, TIME_ONESHOT);
// create the tree
CAPTIONLOOK cl; cl.dwMask = CLMF_TEXTCOLOR | CLMF_FONT | CLMF_LINECOLOR; cl.rgbTextColor = m_puig->GetTextColor(UIE_CALLOUT); cl.rgbLineColor = m_puig->GetPenColor(UIE_BORDER); cl.hFont = (HFONT)m_puig->GetFont(UIE_ACTION); m_Tree.SetDefCaptionLook(cl); cl.rgbTextColor = m_puig->GetTextColor(UIE_CALLOUTHIGH); m_Tree.SetDefCaptionLook(cl, TRUE); m_Tree.SetBkColor(RGB(0,0,0)); if (m_puig->InEditMode()) { m_Tree.Create(m_hWnd, g_TreeRect, TRUE, TRUE); m_Tree.SetScrollBarColors( m_puig->GetBrushColor(UIE_SBTRACK), m_puig->GetBrushColor(UIE_SBTHUMB), m_puig->GetPenColor(UIE_SBBUTTON)); } }
void CDIDeviceActionConfigPage::InitResources() { // create glyphs
if (!m_pbmRelAxesGlyph) m_pbmRelAxesGlyph = CBitmap::CreateFromResource(g_hModule, IDB_AXESGLYPH); if (!m_pbmAbsAxesGlyph) m_pbmAbsAxesGlyph = CBitmap::CreateFromResource(g_hModule, IDB_AXESGLYPH); if (!m_pbmButtonGlyph) m_pbmButtonGlyph = CBitmap::CreateFromResource(g_hModule, IDB_BUTTONGLYPH); if (!m_pbmHatGlyph) m_pbmHatGlyph = CBitmap::CreateFromResource(g_hModule, IDB_HATGLYPH); if (!m_pbmCheckGlyph) m_pbmCheckGlyph = CBitmap::CreateFromResource(g_hModule, IDB_CHECKGLYPH); if (!m_pbmCheckGlyphDark) m_pbmCheckGlyphDark = CBitmap::CreateFromResource(g_hModule, IDB_CHECKGLYPHDARK);
// create IB bitmaps
if (!m_pbmIB) m_pbmIB = CBitmap::CreateFromResource(g_hModule, IDB_IB); if (!m_pbmIB2) m_pbmIB2 = CBitmap::CreateFromResource(g_hModule, IDB_IB2); }
void CDIDeviceActionConfigPage::FreeResources() { if (m_pbmRelAxesGlyph) delete m_pbmRelAxesGlyph; if (m_pbmAbsAxesGlyph) delete m_pbmAbsAxesGlyph; if (m_pbmButtonGlyph) delete m_pbmButtonGlyph; if (m_pbmHatGlyph) delete m_pbmHatGlyph; if (m_pbmCheckGlyph) delete m_pbmCheckGlyph; if (m_pbmCheckGlyphDark) delete m_pbmCheckGlyphDark; if (m_pbmIB) delete m_pbmIB; if (m_pbmIB2) delete m_pbmIB2; m_pbmRelAxesGlyph = NULL; m_pbmAbsAxesGlyph = NULL; m_pbmButtonGlyph = NULL; m_pbmHatGlyph = NULL; m_pbmCheckGlyph = NULL; m_pbmCheckGlyphDark = NULL; m_pbmIB = NULL; m_pbmIB2 = NULL; }
void CDIDeviceActionConfigPage::ClearTree() { m_Tree.FreeAll(); m_pRelAxesParent = NULL; m_pAbsAxesParent = NULL; m_pButtonParent = NULL; m_pHatParent = NULL; m_pUnknownParent = NULL; m_dwLastControlType = 0; }
void CDIDeviceActionConfigPage::InitTree(BOOL bForceInit) { // get type of control
DWORD dwControlType = 0; if (m_pCurControl && m_pCurControl->IsOffsetAssigned()) { DWORD dwObjId = m_pCurControl->GetOffset();
if (dwObjId & DIDFT_RELAXIS) dwControlType = DIDFT_RELAXIS; else if (dwObjId & DIDFT_ABSAXIS) dwControlType = DIDFT_ABSAXIS; else if (dwObjId & DIDFT_BUTTON) dwControlType = DIDFT_BUTTON; else if (dwObjId & DIDFT_POV) dwControlType = DIDFT_POV; }
// Turn off the tree's readonly flag if we are in the assign state.
// We will turn it on later if current control's action has DIA_APPFIXED.
if (m_State == CFGSTATE_NORMAL) m_Tree.SetReadOnly(TRUE); else m_Tree.SetReadOnly(FALSE);
// if this control type is the same as the last, do nothing,
// unless we're force init
if (m_dwLastControlType == dwControlType && !bForceInit && m_State) return;
// delete the whole tree
ClearTree();
// can't use tree if there is no diac or action array
if (m_lpDiac == NULL || m_lpDiac->rgoAction == NULL) return;
// also can't use if we don't have a control type
if (dwControlType == 0) return;
// prepare margin rects
RECT labelmargin = {14, 6, 3, 3}; RECT itemmargin = {14, 1, 3, 2};
// set default indents
m_Tree.SetRootChildIndent(5); m_Tree.SetDefChildIndent(12);
// add the control type sections
m_Tree.SetDefMargin(labelmargin); TCHAR tszResourceString[MAX_PATH]; switch (dwControlType) { case DIDFT_RELAXIS: LoadString(g_hModule, IDS_AXISACTIONS, tszResourceString, MAX_PATH); m_pRelAxesParent = m_Tree.DefAddItem(tszResourceString); break;
case DIDFT_ABSAXIS: LoadString(g_hModule, IDS_AXISACTIONS, tszResourceString, MAX_PATH); m_pAbsAxesParent = m_Tree.DefAddItem(tszResourceString); break;
case DIDFT_BUTTON: LoadString(g_hModule, IDS_BUTTONACTIONS, tszResourceString, MAX_PATH); m_pButtonParent = m_Tree.DefAddItem(tszResourceString); break;
case DIDFT_POV: LoadString(g_hModule, IDS_POVACTIONS, tszResourceString, MAX_PATH); m_pHatParent = m_Tree.DefAddItem(tszResourceString); break;
default: return; }
// populate the tree
m_Tree.SetDefMargin(itemmargin); for (unsigned int i = 0; i < m_lpDiac->dwNumActions; i++) { DIACTIONW *pAction = m_lpDiac->rgoAction + i; CFTItem *pItem = NULL;
if (pAction == NULL) continue;
switch (pAction->dwSemantic & DISEM_TYPE_MASK) { case DISEM_TYPE_AXIS: // Must distinguish between relative and absolute
switch((pAction->dwSemantic & DISEM_REL_MASK) >> DISEM_REL_SHIFT) { case 0: pItem = m_pAbsAxesParent; break; case 1: pItem = m_pRelAxesParent; break; } break; case DISEM_TYPE_BUTTON: pItem = m_pButtonParent; break; case DISEM_TYPE_POV: pItem = m_pHatParent; break; }
if (pItem == NULL) continue;
// Add action with this name
CFTItem *pAlready = GetItemWithActionNameAndSemType(pAction->lptszActionName, pAction->dwSemantic); if (!pAlready) { LPTSTR acname = AllocLPTSTR(pAction->lptszActionName); pItem = m_Tree.DefAddItem(acname, pItem, ATTACH_LASTCHILD); // This might return NULL.
free(acname); if (pItem) pItem->SetUserData((LPVOID)(new RGLPDIACW)); } else { pItem = pAlready; }
if (pItem == NULL) continue;
pItem->SetUserGUID(GUID_ActionItem); RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData(); if (pacs) pacs->SetAtGrow(pacs->GetSize(), pAction);
if (pAlready) { // The tree already has an action with this name. Check the DIA_APPFIXED flag for each DIACTION
// that this item holds.
DWORD dwNumActions = GetNumItemLpacs(pItem); for (DWORD i = 0; i < dwNumActions; ++i) { LPDIACTIONW lpExistingAc = GetItemLpac(pItem, i); // If the DIACTION that is assigned to this device has DIA_APPFIXED flag, then
// the other must have it too.
if (lpExistingAc && IsEqualGUID(lpExistingAc->guidInstance, m_didi.guidInstance)) { if (lpExistingAc->dwFlags & DIA_APPFIXED) { // If this DIACTION has DIA_APPFIXED, then all DIACTIONs must have it too.
for (DWORD j = 0; j < dwNumActions; ++j) { LPDIACTIONW lpChangeAc = GetItemLpac(pItem, j); if (lpChangeAc) lpChangeAc->dwFlags |= DIA_APPFIXED; } }
break; // Break the loop since we already found the DIACTION that is assigned.
} } } // if (pAlready)
}
// show all
m_Tree.GetRoot()->ExpandAll(); m_dwLastControlType = dwControlType; }
int CompareActionNames(LPCWSTR acname1, LPCWSTR acname2) { #ifdef CFGUI__COMPAREACTIONNAMES_CASE_INSENSITIVE
return _wcsicmp(acname1, acname2); #else
return wcscmp(acname1, acname2); #endif
}
CFTItem *CDIDeviceActionConfigPage::GetItemWithActionNameAndSemType(LPCWSTR acname, DWORD dwSemantic) { CFTItem *pItem = m_Tree.GetFirstItem(); for (; pItem != NULL; pItem = pItem->GetNext()) { if (!pItem->IsUserGUID(GUID_ActionItem)) continue; LPDIACTIONW lpac = GetItemLpac(pItem); if (!lpac) continue;
// Check semantic type
if ((lpac->dwSemantic & DISEM_TYPE_MASK) != (dwSemantic & DISEM_TYPE_MASK)) continue;
// If both are axis, check for relative/absolute
if ((lpac->dwSemantic & DISEM_TYPE_MASK) == DISEM_TYPE_AXIS) if ((lpac->dwSemantic & DISEM_REL_MASK) != (dwSemantic & DISEM_REL_MASK)) continue;
// Check name
if (CompareActionNames(lpac->lptszActionName, acname) == 0) return pItem; }
return NULL; }
void CDIDeviceActionConfigPage::OnPaint(HDC hDC) { TCHAR tszResourceString[MAX_PATH]; CPaintHelper ph(*m_puig, hDC);
ph.SetBrush(UIB_BLACK); RECT rect; GetClientRect(&rect); ph.Rectangle(rect, UIR_SOLID);
ph.SetText(UIC_BORDER, UIC_BLACK); //@@BEGIN_MSINTERNAL
#ifdef DDKBUILD
if (!m_puig->QueryAllowEditLayout()) #endif
//@@END_MSINTERNAL
{ rect = g_UserNamesTitleRect; LoadString(g_hModule, IDS_PLAYER_TITLE, tszResourceString, MAX_PATH); DrawText(hDC, tszResourceString, -1, &rect, DT_CENTER|DT_NOCLIP|DT_NOPREFIX); } if (m_puig->GetNumMasterAcFors() > 1) { rect = g_GenresTitleRect; LoadString(g_hModule, IDS_GENRE_TITLE, tszResourceString, MAX_PATH); DrawText(hDC, tszResourceString, -1, &rect, DT_CENTER|DT_NOCLIP|DT_NOPREFIX); }
// Draw tree window title and outline if we are in edit mode.
if (m_puig->InEditMode()) { COLORREF BorderColor = m_puig->GetColor(UIC_BORDER); if (m_Tree.GetReadOnly()) BorderColor = RGB(GetRValue(BorderColor)>>1, GetGValue(BorderColor)>>1, GetBValue(BorderColor)>>1);
::SetTextColor(hDC, BorderColor); // Use the muted color if tree is read only.
// Draw tree window title (Available Actions)
rect = g_TreeTitleRect; LoadString(g_hModule, IDS_AVAILABLEACTIONS_TITLE, tszResourceString, MAX_PATH); DrawText(hDC, tszResourceString, -1, &rect, DT_CENTER|DT_NOCLIP|DT_NOPREFIX); // Draw tree window outline
HGDIOBJ hPen, hOldPen; if (m_Tree.GetReadOnly()) { hPen = CreatePen(PS_SOLID, 0, BorderColor); hOldPen = ::SelectObject(hDC, hPen); } else ph.SetPen(UIP_BORDER);
RECT rc = g_TreeRect; InflateRect(&rc, 1, 1); Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom); if (m_Tree.GetReadOnly()) { ::SelectObject(hDC, hOldPen); DeleteObject(hPen); } }
if (m_pDeviceUI->GetNumViews() < 2) return;
if (m_pbmIB != NULL) m_pbmIB->Draw(hDC, m_ptIBOffset); if (m_pbmIB2 != NULL) m_pbmIB2->Draw(hDC, m_ptIBOffset2); if (m_tszIBText != NULL) { ph.SetElement(UIE_VIEWSEL); RECT rect = m_rectIBText; DrawText(hDC, m_tszIBText, -1, &rect, DT_NOCLIP | DT_NOPREFIX); } }
void CDIDeviceActionConfigPage::SetCurrentControl(CDeviceControl *pControl) { // If the new control is the same as the old, no need to do anything.
if (m_pCurControl == pControl) return; if (m_pCurControl != NULL) { m_pCurControl->Unhighlight(); // If we don't have a current control, then invalidate the view so that the old callout can be repainted.
// If there is a current control, the view will be invalidated by Highlight().
if (!pControl) m_pCurControl->Invalidate(); } m_pCurControl = pControl; if (m_pCurControl != NULL) m_pCurControl->Highlight(); ShowCurrentControlAssignment(); }
CFTItem *CDIDeviceActionConfigPage::GetItemForActionAssignedToControl(CDeviceControl *pControl) { if (!pControl) return NULL;
// find the item for the action assigned to this control, if any
CFTItem *pItem = m_Tree.GetFirstItem(); for (; pItem != NULL; pItem = pItem->GetNext()) { if (!pItem->IsUserGUID(GUID_ActionItem)) continue;
for (int i = 0, n = GetNumItemLpacs(pItem); i < n; i++) { LPDIACTIONW lpac = GetItemLpac(pItem, i); if (!lpac) continue;
if (IsEqualGUID(lpac->guidInstance, m_didi.guidInstance) && GetOffset(lpac) == pControl->GetOffset()) return pItem; } }
return NULL; }
int CDIDeviceActionConfigPage::GetNumItemLpacs(CFTItem *pItem) { if (pItem == NULL) return 0;
RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData(); if (!pacs) return 0; else return pacs->GetSize(); }
LPDIACTIONW CDIDeviceActionConfigPage::GetItemLpac(CFTItem *pItem, int i) { if (pItem == NULL) return NULL;
RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData(); if (!pacs || i < 0 || i >= pacs->GetSize()) return NULL; else return pacs->GetAt(i); }
void CDIDeviceActionConfigPage::ShowCurrentControlAssignment() { // init the tree
InitTree();
// if we don't have a control...
if (m_pCurControl == NULL) { // select nothing
m_Tree.SetCurSel(NULL); return; }
// find the item for the action assigned to this control, if any
CFTItem *pItem = GetItemForActionAssignedToControl(m_pCurControl);
// if we didn't find a match...
if (!pItem) { // select nothing
m_Tree.SetCurSel(NULL); return; }
// We need to check if the action this control is assigned to has DIA_APPFIXED flag.
// If it does, this control cannot be remapped to another action.
// We prevent this by setting the tree control to read-only, so it can't receive any clicks.
LPDIACTIONW lpAc = GetItemLpac(pItem); // Get the action
if (lpAc && (lpAc->dwFlags & DIA_APPFIXED)) m_Tree.SetReadOnly(TRUE);
// otherwise, show item and select it
pItem->EnsureVisible(); m_Tree.SetCurSel(pItem); }
void CDIDeviceActionConfigPage::DeviceUINotify(const DEVICEUINOTIFY &uin) { switch (uin.msg) { case DEVUINM_NUMVIEWSCHANGED: Invalidate(); break;
case DEVUINM_SELVIEW: // set the view
m_pDeviceUI->SetView(uin.selview.nView);
// show the assignments for the controls
SetControlAssignments();
// select nothing
SetCurrentControl(NULL); break;
case DEVUINM_ONCONTROLDESTROY: if (uin.control.pControl == m_pCurControl) m_pCurControl = NULL; break;
case DEVUINM_CLICK: ExitAssignState(); switch (uin.from) { case DEVUINFROM_CONTROL: SetCurrentControl(uin.control.pControl); SetAppropriateDefaultText(); break; case DEVUINFROM_VIEWWND: break; } break;
case DEVUINM_DOUBLECLICK: switch (uin.from) { case DEVUINFROM_CONTROL: EnterAssignState(); break; } break;
case DEVUINM_MOUSEOVER: SetAppropriateDefaultText(); break;
case DEVUINM_RENEWDEVICE: HWND hParent = GetParent(m_hWnd); CConfigWnd *pCfgWnd = (CConfigWnd *)GetFlexWnd(hParent); if (pCfgWnd) { LPDIRECTINPUTDEVICE8W lpDID = pCfgWnd->RenewDevice(m_didi.guidInstance); if (lpDID) { // Destroy the device instance we have
if (m_lpDID) m_lpDID->Release(); lpDID->AddRef(); m_lpDID = lpDID; } m_pDeviceUI->SetDevice(lpDID); // Sets the device pointer in CDeviceUI (no need to AddRef)
} } }
void CDIDeviceActionConfigPage::UnassignCallout() { // find the item for the action assigned to this control, if any
CFTItem *pItem = GetItemForActionAssignedToControl(m_pCurControl); if (pItem) { LPDIACTIONW lpac = GetItemLpac(pItem); // Only unassign if the action doesn't have DIA_APPFIXED flag.
if (lpac && !(lpac->dwFlags & DIA_APPFIXED)) { ActionClick(NULL); m_Tree.Invalidate(); } } // Sort the list if the check box is checked.
if (m_CheckBox.GetCheck()) m_pDeviceUI->GetCurView()->SortAssigned(TRUE); }
void CDIDeviceActionConfigPage::NullAction(LPDIACTIONW lpac) { if (lpac == NULL) return;
SetInvalid(lpac);
//@@BEGIN_MSINTERNAL
// TODO: find tree view item with this action and indicate unassignment
//@@END_MSINTERNAL
}
void CDIDeviceActionConfigPage::UnassignActionsAssignedTo(const GUID &guidInstance, DWORD dwOffset) { if (m_lpDiac == NULL || m_lpDiac->rgoAction == NULL) return;
if (IsEqualGUID(guidInstance, GUID_NULL)) return;
// assign any actions assigned to this control to nothing
DWORD i; LPDIACTIONW lpac; for (i = 0, lpac = m_lpDiac->rgoAction; i < m_lpDiac->dwNumActions; i++, lpac++) if (IsEqualGUID(guidInstance, lpac->guidInstance) && dwOffset == GetOffset(lpac)/*->dwInternalOffset*/) { GlobalUnassignControlAt(guidInstance, dwOffset); NullAction(lpac); } }
void CDIDeviceActionConfigPage::UnassignControl(CDeviceControl *pControl) { if (pControl == NULL) return;
// make sure the control itself indicates unassignment
pControl->SetCaption(g_tszUnassignedControlCaption); }
void CallUnassignControl(CDeviceControl *pControl, LPVOID pVoid, BOOL bFixed) { CDIDeviceActionConfigPage *pThis = (CDIDeviceActionConfigPage *)pVoid; pThis->UnassignControl(pControl); }
void CDIDeviceActionConfigPage::GlobalUnassignControlAt(const GUID &guidInstance, DWORD dwOffset) { if (IsEqualGUID(guidInstance, GUID_NULL)) return;
if (IsEqualGUID(guidInstance, m_didi.guidInstance)) m_pDeviceUI->DoForAllControlsAtOffset(dwOffset, CallUnassignControl, this); }
// this function must find whatever control is assigned to this action and unassign it
void CDIDeviceActionConfigPage::UnassignAction(LPDIACTIONW slpac) { // call UnassignSpecificAction for each action with the same name
// as this one, including this one
if (slpac == NULL) return;
CFTItem *pItem = GetItemWithActionNameAndSemType(slpac->lptszActionName, slpac->dwSemantic); if (!pItem) return;
RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData(); if (!pacs) return;
for (int i = 0; i < pacs->GetSize(); i++) UnassignSpecificAction(pacs->GetAt(i)); }
void CDIDeviceActionConfigPage::UnassignSpecificAction(LPDIACTIONW lpac) { if (lpac == NULL) return;
if (IsEqualGUID(lpac->guidInstance, GUID_NULL)) return;
// if there's a control with this instance/offset, unassign it
UnassignActionsAssignedTo(lpac->guidInstance, GetOffset(lpac)/*->dwInternalOffset*/); GlobalUnassignControlAt(lpac->guidInstance, GetOffset(lpac)/*->dwInternalOffset*/);
// now actually null the action
NullAction(lpac); }
void CDIDeviceActionConfigPage::AssignCurrentControlToAction(LPDIACTIONW lpac) { // if there is a control, unassign it
if (m_pCurControl != NULL) { UnassignControl(m_pCurControl); GUID guidInstance; DWORD dwOffset; m_pCurControl->GetInfo(guidInstance, dwOffset); UnassignActionsAssignedTo(guidInstance, dwOffset); }
// if there is an action, unassign it
if (lpac != NULL) UnassignAction(lpac);
// can only continue if we have both
if (lpac == NULL || m_pCurControl == NULL) return;
// here we should have a control and an action
assert(lpac != NULL); assert(m_pCurControl != NULL);
// because an action can only be assigned to one control,
// make sure this action is unassigned first
UnassignAction(lpac);
// now actually assign
DWORD ofs; m_pCurControl->GetInfo(lpac->guidInstance, ofs/*lpac->dwInternalOffset*/); SetOffset(lpac, ofs); LPTSTR acname = AllocLPTSTR(lpac->lptszActionName); m_pCurControl->SetCaption(acname, lpac->dwFlags & DIA_APPFIXED); free(acname);
// Sort the action list if check box is checked
if (m_CheckBox.GetCheck()) { m_pDeviceUI->GetCurView()->SortAssigned(TRUE); // Scroll so that we scroll to make this visible since it might be displaced by sorting.
m_pDeviceUI->GetCurView()->ScrollToMakeControlVisible(m_pCurControl->GetCalloutMaxRect()); } }
void CDIDeviceActionConfigPage::ActionClick(LPDIACTIONW lpac) { if (m_pCurControl != NULL) { AssignCurrentControlToAction(lpac);
// Set assignment since other views may have the same callout and
// they need to be updated too.
SetControlAssignments(); } // Change the state back to normal
ExitAssignState(); }
void CDIDeviceActionConfigPage::SetControlAssignments() { assert(!IsEqualGUID(m_didi.guidInstance, GUID_NULL));
m_pDeviceUI->SetAllControlCaptionsTo(g_tszUnassignedControlCaption);
if (m_lpDiac == NULL || m_lpDiac->rgoAction == NULL) return;
DWORD i; LPDIACTIONW lpac; for (i = 0, lpac = m_lpDiac->rgoAction; i < m_lpDiac->dwNumActions; i++) { lpac = m_lpDiac->rgoAction + i;
if (IsEqualGUID(lpac->guidInstance, GUID_NULL)) continue;
if (!IsEqualGUID(lpac->guidInstance, m_didi.guidInstance)) continue;
LPTSTR acname = AllocLPTSTR(lpac->lptszActionName); m_pDeviceUI->SetCaptionForControlsAtOffset(GetOffset(lpac)/*->dwInternalOffset*/, acname, lpac->dwFlags & DIA_APPFIXED); free(acname); } }
void CDIDeviceActionConfigPage::DoViewSel() { m_ViewSelWnd.Go(m_hWnd, m_rectIB.left, m_rectIB.top, m_pDeviceUI); }
void CDIDeviceActionConfigPage::OnClick(POINT point, WPARAM, BOOL bLeft) { if (!bLeft) return;
// Unhighlight current callout
ExitAssignState();
if (m_pDeviceUI->GetNumViews() > 1) { int iCurView = m_pDeviceUI->GetCurViewIndex();
if (PtInRect(&m_rectIBLeft, point)) m_pDeviceUI->SetView(iCurView == 0 ? m_pDeviceUI->GetNumViews() - 1 : iCurView - 1); if (PtInRect(&m_rectIBRight, point)) m_pDeviceUI->SetView(iCurView == m_pDeviceUI->GetNumViews() - 1 ? 0 : iCurView + 1); if (PtInRect(&m_rectIBText, point)) DoViewSel(); } }
void CDIDeviceActionConfigPage::OnMouseOver(POINT point, WPARAM fwKeys) { CFlexWnd::s_ToolTip.SetEnable(FALSE);
// Check view selection area so we can display text in info box.
if (m_pDeviceUI->GetNumViews() > 1) { if (PtInRect(&m_rectIB, point)) { SetInfoText(IDS_INFOMSG_VIEW_VIEWSEL); return; } }
SetAppropriateDefaultText(); }
int GetActionIndexFromPointer(LPDIACTIONW p, LPDIACTIONFORMATW paf) { if (!p || !paf || !paf->rgoAction) return -1;
int index = int((((LPBYTE)p) - ((LPBYTE)paf->rgoAction)) / (DWORD)sizeof(DIACTIONW));
assert(&(paf->rgoAction[index]) == p);
return index; }
BOOL CDIDeviceActionConfigPage::IsActionAssignedHere(int index) { if (!m_lpDiac) return FALSE;
if (index < 0 || index >= (int)m_lpDiac->dwNumActions) return FALSE;
return IsEqualGUID(m_didi.guidInstance, m_lpDiac->rgoAction[index].guidInstance); }
LRESULT CDIDeviceActionConfigPage::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_UNHIGHLIGHT: // Unhighlight current callout
ExitAssignState(); break;
case WM_KEYDOWN: #ifdef DBG
// In debug version, shift-escape exits the UI.
if (wParam == VK_ESCAPE && GetAsyncKeyState(VK_SHIFT) < 0) { PostMessage(GetParent(m_hWnd), WM_KEYDOWN, wParam, lParam); break; } #endif
// If this is a keyboard device, then click-to-pick will take care of the functionalities below.
// Process WM_KEYDOWN only for non-keyboard devices.
if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_KEYBOARD) return 0; switch(wParam) { case VK_RETURN: // If we are not in assign state, enter it.
if (m_State == CFGSTATE_NORMAL && m_pCurControl) EnterAssignState(); break;
case VK_DELETE: // If we are in assign state and there is a control, unassign it.
if (m_State == CFGSTATE_ASSIGN && m_pCurControl) UnassignCallout(); break;
case VK_ESCAPE: if (m_State == CFGSTATE_ASSIGN) ExitAssignState();
break; } return 0;
case WM_FLEXCHECKBOX: switch(wParam) { case CHKNOTIFY_UNCHECK: //@@BEGIN_MSINTERNAL
#ifdef DDKBUILD
if (!m_pDeviceUI->InEditMode()) // Ignore sort assigned checkbox if in DDK tool
{ #endif
//@@END_MSINTERNAL
m_pDeviceUI->GetCurView()->SortAssigned(FALSE); if (m_pCurControl) { // Scroll so that we scroll to make this visible since it might be displaced by sorting.
m_pDeviceUI->GetCurView()->ScrollToMakeControlVisible(m_pCurControl->GetCalloutMaxRect()); } Invalidate(); //@@BEGIN_MSINTERNAL
#ifdef DDKBUILD
} #endif
//@@END_MSINTERNAL
break; case CHKNOTIFY_CHECK: //@@BEGIN_MSINTERNAL
#ifdef DDKBUILD
if (!m_pDeviceUI->InEditMode()) // Ignore sort assigned checkbox if in DDK tool
{ #endif
//@@END_MSINTERNAL
m_pDeviceUI->GetCurView()->SortAssigned(TRUE); if (m_pCurControl) { // Scroll so that we scroll to make this visible since it might be displaced by sorting.
m_pDeviceUI->GetCurView()->ScrollToMakeControlVisible(m_pCurControl->GetCalloutMaxRect()); } Invalidate(); //@@BEGIN_MSINTERNAL
#ifdef DDKBUILD
} #endif
//@@END_MSINTERNAL
break; case CHKNOTIFY_MOUSEOVER: SetInfoText(m_CheckBox.GetCheck() ? IDS_INFOMSG_VIEW_SORTENABLED : IDS_INFOMSG_VIEW_SORTDISABLED); break; } break;
case WM_FLEXCOMBOBOX: switch (wParam) { case FCBN_MOUSEOVER: if (lParam) { CFlexComboBox *pCombo = (CFlexComboBox*)lParam; if (pCombo->m_hWnd == m_UserNames.m_hWnd) SetInfoText(m_puig->InEditMode() ? IDS_INFOMSG_EDIT_USERNAME : IDS_INFOMSG_VIEW_USERNAME); else if (pCombo->m_hWnd == m_Genres.m_hWnd) SetInfoText(m_puig->InEditMode() ? IDS_INFOMSG_EDIT_GAMEMODE : IDS_INFOMSG_VIEW_GAMEMODE); } break;
case FCBN_SELCHANGE: // Clear the tool tip as the combo-box has closed
CFlexWnd::s_ToolTip.SetEnable(FALSE); CFlexWnd::s_ToolTip.SetToolTipParent(NULL); if (m_pUIFrame && m_puig) { ExitAssignState(); m_pUIFrame->SetCurGenre(m_Genres.GetSel()); int nUser = m_UserNames.GetSel(); if (m_puig->GetNumUserNames() > 0 && nUser >= m_puig->GetNumUserNames()) nUser = -1; m_pUIFrame->SetCurUser(m_nPageIndex, nUser); } break; } return 0;
case WM_FLEXTREENOTIFY: { // Check if this is a mouse over message (just for info box update)
if (wParam == FTN_MOUSEOVER) { SetAppropriateDefaultText(); return FALSE; }
if (!lParam) return FALSE; FLEXTREENOTIFY &n = *((FLEXTREENOTIFY *)(LPVOID)lParam); if (!n.pItem) return FALSE; switch (wParam) { case FTN_OWNERDRAW: { POINT ofs = {0, 0}; CBitmap *pbmGlyph = NULL; BOOL bAssigned = FALSE, bAssignedHere = FALSE; if (n.pItem->IsUserGUID(GUID_ActionItem)) { LPDIACTIONW lpac = GetItemLpac(n.pItem, 0); if (lpac) // We now walk through each DIACTION and find those with action name match, then see if
// they are assigned anywhere.
for (DWORD i = 0; i < m_lpDiac->dwNumActions; ++i) { if (wcscmp(lpac->lptszActionName, m_lpDiac->rgoAction[i].lptszActionName)) continue;
if (bAssignedHere = IsActionAssignedHere(i)) { bAssigned = TRUE; break; } if (m_pUIFrame && m_pUIFrame->QueryActionAssignedAnywhere(m_didi.guidInstance, i) == S_OK) bAssigned = TRUE; } if (bAssigned || bAssignedHere) { pbmGlyph = bAssignedHere ? m_pbmCheckGlyph : m_pbmCheckGlyphDark; pbmGlyph->FigureSize(); ofs.x = 2; ofs.y = 4; } } else { if (n.pItem == m_pRelAxesParent) pbmGlyph = m_pbmRelAxesGlyph; if (n.pItem == m_pAbsAxesParent) pbmGlyph = m_pbmAbsAxesGlyph; if (n.pItem == m_pButtonParent) pbmGlyph = m_pbmButtonGlyph; if (n.pItem == m_pHatParent) pbmGlyph = m_pbmHatGlyph; ofs.y = 2; } if (!pbmGlyph) return FALSE; n.pItem->PaintInto(n.hDC); RECT rect; CPaintHelper ph(*m_puig, n.hDC); ph.SetElement(UIE_GLYPH); n.pItem->GetMargin(rect); pbmGlyph->Draw(n.hDC, ofs.x, rect.top + ofs.y); return TRUE; }
case FTN_CLICK: // We cannot assign a different control to this action if it has the DIA_APPFIXED flag.
if (n.pItem->IsUserGUID(GUID_ActionItem) && GetItemLpac(n.pItem) && !(GetItemLpac(n.pItem)->dwFlags & DIA_APPFIXED)) { m_Tree.SetCurSel(n.pItem); ActionClick(GetItemLpac(n.pItem)); } else { #ifdef CFGUI__ALLOW_USER_ACTION_TREE_BRANCH_MANIPULATION
if (!n.pItem->IsExpanded()) n.pItem->Expand(); else n.pItem->Collapse(); #endif
} break; } break; } }
return CFlexWnd::WndProc(hWnd, msg, wParam, lParam); }
void CDIDeviceActionConfigPage::SetInvalid(LPDIACTIONW lpac) { lpac->guidInstance = GUID_NULL; lpac->dwObjID = (DWORD)-1; }
DWORD CDIDeviceActionConfigPage::GetOffset(LPDIACTIONW lpac) { return lpac ? lpac->dwObjID : (DWORD)-1; }
void CDIDeviceActionConfigPage::SetOffset(LPDIACTIONW lpac, DWORD ofs) { assert(lpac != NULL); if (!lpac) return; lpac->dwObjID = ofs; }
HRESULT CDIDeviceActionConfigPage::InitLookup() { DIDEVOBJSTRUCT os;
HRESULT hresult = FillDIDeviceObjectStruct(os, m_lpDID);
if (FAILED(hresult)) return hresult;
for (int i = 0; i < os.nObjects; i++) { DIDEVICEOBJECTINSTANCEW &doi = os.pdoi[i]; offset_objid.add(doi.dwOfs, doi.dwType); }
return S_OK; }
HRESULT CDIDeviceActionConfigPage::SetEditLayout(BOOL bEditLayout) { m_pDeviceUI->SetEditMode(bEditLayout); return S_OK; }
//@@BEGIN_MSINTERNAL
#ifdef DDKBUILD
HRESULT CDIDeviceActionConfigPage::WriteIHVSetting() { m_pDeviceUI->WriteToINI(); return S_OK; } #endif
//@@END_MSINTERNAL
BOOL CDIDeviceActionConfigPage::IsControlMapped(CDeviceControl *pControl) { if (pControl == NULL) return FALSE;
if (!pControl->IsOffsetAssigned()) return FALSE;
if (m_lpDiac == NULL) return FALSE; for (DWORD i = 0; i < m_lpDiac->dwNumActions; i++) if (GetOffset(&(m_lpDiac->rgoAction[i])) == pControl->GetOffset()) return TRUE;
return FALSE; }
void CDIDeviceActionConfigPage::InitDevice() { if (m_lpDID == NULL || m_pDeviceUI == NULL || m_pUIFrame == NULL) return;
HWND hWndMain = m_pUIFrame->GetMainHWND(); if (!hWndMain) return;
// don't do anything if this is a mouse
switch ((DWORD)(LOBYTE(LOWORD(m_pDeviceUI->m_didi.dwDevType)))) { case DI8DEVTYPE_MOUSE: return; }
// init/prepare...
int i; const DIDEVOBJSTRUCT &os = m_pDeviceUI->m_os; int nObjects = os.nObjects;
DIDATAFORMAT df; df.dwSize = sizeof(DIDATAFORMAT); df.dwObjSize = sizeof(DIOBJECTDATAFORMAT); df.dwFlags = DIDF_ABSAXIS; df.dwDataSize = sizeof(DWORD) * (DWORD)nObjects; df.dwNumObjs = (DWORD)nObjects; df.rgodf = (DIOBJECTDATAFORMAT *)malloc(sizeof(DIOBJECTDATAFORMAT) * nObjects); if (df.rgodf == NULL) { etrace1(_T("Could not allocate DIOBJECTDATAFORMAT array of %d elements\n"), nObjects); return; }
m_cbDeviceDataSize = df.dwDataSize; for (int c = 0; c < 2; c++) { if (m_pDeviceData[c] != NULL) free(m_pDeviceData[c]); m_pDeviceData[c] = (DWORD *)malloc(m_cbDeviceDataSize); if (m_pDeviceData[c] == NULL) etrace2(_T("Could not allocate device data buffer %d of %d bytes\n"), c, m_cbDeviceDataSize); } m_nOnDeviceData = 0; m_bFirstDeviceData = TRUE;
for (i = 0; i < nObjects; i++) { DIOBJECTDATAFORMAT *podf = &(df.rgodf[i]); podf->pguid = NULL; podf->dwOfs = i * sizeof(DWORD); podf->dwType = os.pdoi[i].dwType; podf->dwFlags = 0; }
if (df.rgodf != NULL) { HRESULT hr = m_lpDID->SetDataFormat(&df); free(df.rgodf); df.rgodf = NULL;
if (FAILED(hr)) { etrace1(_T("SetDataFormat() failed, returning 0x%08x\n"), hr); } else { hr = m_lpDID->SetCooperativeLevel(hWndMain, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE); if (FAILED(hr)) etrace1(_T("SetCooperativeLevel() failed, returning 0x%08x\n"), hr);
DIPROPRANGE range; range.diph.dwSize = sizeof(DIPROPRANGE); range.diph.dwHeaderSize = sizeof(DIPROPHEADER); range.diph.dwObj = 0; range.diph.dwHow = DIPH_DEVICE; range.lMin = DEVICE_POLLING_AXIS_MIN; range.lMax = DEVICE_POLLING_AXIS_MAX;
hr = m_lpDID->SetProperty(DIPROP_RANGE, (LPCDIPROPHEADER)&range); if (FAILED(hr)) etrace1(_T("SetProperty(DIPROP_RANGE, ...) failed, returning 0x%08x\n"), hr);
hr = m_lpDID->Acquire(); if (FAILED(hr)) etrace1(_T("Acquire() failed, returning 0x%08x\n"), hr); } } }
void CALLBACK CDIDeviceActionConfigPage::DeviceTimerProc(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
CDIDeviceActionConfigPage *pPage = (CDIDeviceActionConfigPage *)GetFlexWnd((HWND)dwUser); // Get flex object
if (pPage) pPage->DeviceTimer(); }
void CDIDeviceActionConfigPage::DeviceTimer() { DWORD *pOldData = m_pDeviceData[m_nOnDeviceData]; m_nOnDeviceData = (m_nOnDeviceData + 1) & 1; DWORD *pData = m_pDeviceData[m_nOnDeviceData];
if (m_lpDID == NULL || pData == NULL || pOldData == NULL) { // Required data not available. Return and there'll be no more timer callbacks.
etrace(_T("DeviceTimer() failed\n")); return; }
// Get device data only if this page is visible.
if (m_lpDiac) { HRESULT hr = m_lpDID->Poll(); if (SUCCEEDED(hr)) { hr = m_lpDID->GetDeviceState(m_cbDeviceDataSize, pData); if (SUCCEEDED(hr)) { if (!m_bFirstDeviceData) { DeviceDelta(pData, pOldData); } else { m_bFirstDeviceData = FALSE; } } else { etrace1(_T("GetDeviceState() failed, returning 0x%08x\n"), hr); } } else { etrace1(_T("Poll() failed, returning 0x%08x\n"), hr); } }
// Set the next timer event.
if (g_fptimeSetEvent) g_fptimeSetEvent(DEVICE_POLLING_INTERVAL, DEVICE_POLLING_INTERVAL, CDIDeviceActionConfigPage::DeviceTimerProc, (DWORD_PTR)m_hWnd, TIME_ONESHOT); }
void CDIDeviceActionConfigPage::DeviceDelta(DWORD *pData, DWORD *pOldData) { if (pData == NULL || pOldData == NULL || m_pDeviceUI == NULL) return;
const DIDEVOBJSTRUCT &os = m_pDeviceUI->m_os;
// see which objects changed
for (int i = 0; i < os.nObjects; i++) { // for axes, we need to do special processing
if (os.pdoi[i].dwType & DIDFT_AXIS) { BOOL bSig = FALSE, bOldSig = FALSE;
StoreAxisDeltaAndCalcSignificance(os.pdoi[i], pData[i], pOldData[i], bSig, bOldSig);
AxisDelta(os.pdoi[i], bSig, bOldSig);
continue; }
// for all others, skip that which didn't change
if (pData[i] == pOldData[i]) continue;
// pass to appropriate delta function
DWORD dwObjId = os.pdoi[i].dwType; if (dwObjId & DIDFT_BUTTON) ButtonDelta(os.pdoi[i], pData[i], pOldData[i]); else if (dwObjId & DIDFT_POV) PovDelta(os.pdoi[i], pData[i], pOldData[i]); } }
void CDIDeviceActionConfigPage::StoreAxisDeltaAndCalcSignificance(const DIDEVICEOBJECTINSTANCEW &doi, DWORD data, DWORD olddata, BOOL &bSig, BOOL &bOldSig) { // see if this object has an axis value array
int i; if (objid_avai.getright(doi.dwType, i)) { AxisValueArray &ar = m_AxisValueArray[i]; int on = ar[0] + 1; if (on >= ar.GetSize()) on = DEVICE_POLLING_ACBUF_START_INDEX; ar[0] = on; int delta = abs(int(data) - int(olddata)); // Scale up the delta if this is a wheel axis as wheels are harder to move generally.
if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_DRIVING && doi.guidType == GUID_XAxis) delta = delta * DEVICE_POLLING_WHEEL_SCALE_FACTOR; if (delta < DEVICE_POLLING_AXIS_MINDELTA) delta = 0; int cumul = ar[1]; // Retrieve cumulative value for easier processing
cumul -= ar[on]; // Subtract value in current slot from cumul since it's being thrown away.
cumul += delta; // Add current delta to cumul
ar[on] = delta; // Store the delta at current slot
ar[1] = cumul; // Save cumulative value
bOldSig = (BOOL)ar[2]; ar[2] = int(bSig = cumul > DEVICE_POLLING_AXIS_SIGNIFICANT); if (bSig) { // This axis is about to be activated. We now reset the history and cumulative movement since we don't need them any more.
ar[0] = DEVICE_POLLING_ACBUF_START_INDEX; ar[1] = 0; ar[2] = FALSE; for (int c = DEVICE_POLLING_ACBUF_START_INDEX; c < DEVICE_POLLING_ACBUF_START_INDEX + DEVICE_POLLING_AXIS_ACCUMULATION; c++) ar[c] = 0; } } else { i = m_AxisValueArray.GetSize(); m_AxisValueArray.SetSize(i + 1); objid_avai.add(doi.dwType, i); AxisValueArray &ar = m_AxisValueArray[i]; ar.SetSize(DEVICE_POLLING_ACBUF_START_INDEX + DEVICE_POLLING_AXIS_ACCUMULATION); ar[0] = DEVICE_POLLING_ACBUF_START_INDEX; ar[1] = 0; ar[2] = FALSE; for (int c = DEVICE_POLLING_ACBUF_START_INDEX; c < DEVICE_POLLING_ACBUF_START_INDEX + DEVICE_POLLING_AXIS_ACCUMULATION; c++) ar[c] = 0; bOldSig = bSig = FALSE; } }
void CDIDeviceActionConfigPage::AxisDelta(const DIDEVICEOBJECTINSTANCEW &doi, BOOL data, BOOL old) { if (data && !old) { if (m_State == CFGSTATE_NORMAL) ActivateObject(doi); } if (old && !data) DeactivateObject(doi); }
void CDIDeviceActionConfigPage::ButtonDelta(const DIDEVICEOBJECTINSTANCEW &doi, DWORD data, DWORD old) { static DWORD dwLastOfs; static DWORD dwLastTimeStamp;
if (data && !old) { // Do special processing for keyboard
if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_KEYBOARD) { // If this is an ENTER key, we enter the assign state if not already in it.
if (doi.dwOfs == DIK_RETURN || doi.dwOfs == DIK_NUMPADENTER) { if (m_State == CFGSTATE_NORMAL && m_pCurControl) EnterAssignState(); return; // Do nothing other than entering the assign state. No highlighting
}
// DELETE key case
// If we are in assign state and there is a control, unassign it.
if (doi.dwOfs == DIK_DELETE && m_State == CFGSTATE_ASSIGN && m_pCurControl) { UnassignCallout(); return; // Don't highlight or do pick to click for delete if this press happens during assign state.
}
// ESCAPE key case
if (doi.dwOfs == DIK_ESCAPE && m_State == CFGSTATE_ASSIGN) { ExitAssignState(); return; }
// For all other keys, still process click-to-pick or highlighting.
}
// Enter assign state if this is a double activation
if (m_State == CFGSTATE_NORMAL) { ActivateObject(doi);
if (doi.dwOfs == dwLastOfs && dwLastTimeStamp + GetDoubleClickTime() > GetTickCount()) { // We check if a callout for this control exists. If not, do not enter assign state.
CDeviceView *pCurView = m_pDeviceUI->GetCurView(); CDeviceControl *pControl = pCurView->GetControlFromOfs(doi.dwType); if (pControl) EnterAssignState(); } dwLastOfs = doi.dwOfs; dwLastTimeStamp = GetTickCount(); } } if (old && !data) DeactivateObject(doi); }
void CDIDeviceActionConfigPage::PovDelta(const DIDEVICEOBJECTINSTANCEW &doi, DWORD data, DWORD old) { BOOL d = data != -1, o = old != -1;
if (d && !o) { if (m_State == CFGSTATE_NORMAL) ActivateObject(doi); } if (o && !d) DeactivateObject(doi); }
void CDIDeviceActionConfigPage::ActivateObject(const DIDEVICEOBJECTINSTANCEW &doi) { if (m_pDeviceUI == NULL) return;
//@@BEGIN_MSINTERNAL
#ifdef DDKBUILD
if (m_pDeviceUI->GetCurView()->InEditState()) return; #endif
//@@END_MSINTERNAL
CDeviceView *pCurView = m_pDeviceUI->GetCurView(), *pView = pCurView; if (pView == NULL) return;
CDeviceControl *pControl = pView->GetControlFromOfs(doi.dwType); if (pControl == NULL) { for (int i = 0; i < m_pDeviceUI->GetNumViews(); i++) { pView = m_pDeviceUI->GetView(i); if (pView == NULL) continue;
pControl = pView->GetControlFromOfs(doi.dwType); if (pControl != NULL) break; }
if (pControl != NULL && pView != NULL && pView != pCurView) { // switch to view
m_pDeviceUI->SetView(pView); SetControlAssignments(); SetCurrentControl(NULL); } }
if (pControl != NULL) SetCurrentControl(pControl);
SetAppropriateDefaultText(); }
void CDIDeviceActionConfigPage::DeactivateObject(const DIDEVICEOBJECTINSTANCEW &doi) { // Add code that needs to be run when deactivating here.
}
HRESULT CDIDeviceActionConfigPage::Unacquire() { if (m_lpDID != NULL) m_lpDID->Unacquire();
return S_OK; }
HRESULT CDIDeviceActionConfigPage::Reacquire() { InitDevice();
return S_OK; }
void CDIDeviceActionConfigPage::EnterAssignState() { if (!m_puig->InEditMode()) return; if (!m_pCurControl || m_pCurControl->IsFixed()) return; SetInfoText(IDS_INFOMSG_EDIT_EDITMODEENABLED); m_State = CFGSTATE_ASSIGN; // Into the assign state.
ShowCurrentControlAssignment(); // Show the tree
m_Tree.Invalidate(); Invalidate(); }
void CDIDeviceActionConfigPage::ExitAssignState() { m_State = CFGSTATE_NORMAL; // Out of the assign state.
SetCurrentControl(NULL); // Unselect the control
ShowCurrentControlAssignment(); // Show the tree
m_Tree.Invalidate(); Invalidate(); SetAppropriateDefaultText(); }
HRESULT CDIDeviceActionConfigPage::SetInfoText(int iCode) { // We check for special code -1 here. This is only called by CConfigWnd, and means that we should
// call SetAppropriateDefaultText to display proper text.
if (iCode == -1) SetAppropriateDefaultText(); else m_InfoBox.SetText(iCode); return S_OK; }
void CDIDeviceActionConfigPage::SetAppropriateDefaultText() { if (m_puig->InEditMode()) { if (m_State == CFGSTATE_ASSIGN) SetInfoText(IDS_INFOMSG_EDIT_EDITMODEENABLED); else if (m_pCurControl) { if (m_pCurControl->IsFixed()) SetInfoText(IDS_INFOMSG_APPFIXEDSELECT); else SetInfoText(IDS_INFOMSG_EDIT_CTRLSELECTED); } else { if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_KEYBOARD) SetInfoText(IDS_INFOMSG_EDIT_KEYBOARD); else if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_MOUSE) SetInfoText(IDS_INFOMSG_EDIT_MOUSE); else SetInfoText(IDS_INFOMSG_EDIT_DEVICE); } } else SetInfoText(IDS_INFOMSG_VIEW_DEVICE); }
|