home *** CD-ROM | disk | FTP | other *** search
/ Beginning Direct3D Game Programming / Direct3D.iso / directx / dxf / samples / multimedia / directinput / diconfig / cdiacpage.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-11  |  49.6 KB  |  1,830 lines

  1. //-----------------------------------------------------------------------------
  2. // File: cdiacpage.cpp
  3. //
  4. // Desc: CDIDeviceActionConfigPage implements the page object used by the UI.
  5. //       A page covers the entire UI minus the device tabs and the bottons at
  6. //       the bottom.  The information window, player combo-box, genre combo-
  7. //       box, action list tree, and device view window are all managed by
  8. //       the page.
  9. //
  10. // Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
  11. //-----------------------------------------------------------------------------
  12.  
  13. #include "common.hpp"
  14. #include <initguid.h>
  15.  
  16. DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
  17.  
  18.  
  19. // {D0B5C9AE-966F-4510-B955-4D2482C5EB1B}
  20. DEFINE_GUID(GUID_ActionItem, 
  21. 0xd0b5c9ae, 0x966f, 0x4510, 0xb9, 0x55, 0x4d, 0x24, 0x82, 0xc5, 0xeb, 0x1b);
  22.  
  23.  
  24. #define DISEM_TYPE_MASK                    ( 0x00000600 )
  25. #define DISEM_REL_MASK                     ( 0x00000100 )
  26. #define DISEM_REL_SHIFT                    ( 8 ) 
  27. #define DISEM_TYPE_AXIS                    0x00000200
  28. #define DISEM_TYPE_BUTTON                  0x00000400
  29. #define DISEM_TYPE_POV                     0x00000600
  30.  
  31. #define DEVICE_POLLING_INTERVAL 10
  32. #define DEVICE_POLLING_AXIS_MIN 0
  33. #define DEVICE_POLLING_AXIS_MAX 100
  34. #define DEVICE_POLLING_AXIS_MINDELTA 3
  35. #define DEVICE_POLLING_AXIS_SIGNIFICANT 40
  36. #define DEVICE_POLLING_AXIS_ACCUMULATION 20
  37. #define DEVICE_POLLING_ACBUF_START_INDEX 3
  38.  
  39. // For WINMM.DLL
  40. HINSTANCE g_hWinMmDLL = NULL;
  41. FUNCTYPE_timeSetEvent g_fptimeSetEvent = NULL;
  42.  
  43. //QueryInterface
  44. STDMETHODIMP CDIDeviceActionConfigPage::QueryInterface(REFIID iid, LPVOID* ppv)
  45. {
  46.    //null the out param
  47.     *ppv = NULL;
  48.  
  49.     if ((iid == IID_IUnknown) || (iid == IID_IDIDeviceActionConfigPage))
  50.     {
  51.         *ppv = this;
  52.         AddRef();
  53.         return S_OK;
  54.     }
  55.  
  56.     return E_NOINTERFACE;
  57. }
  58.  
  59.  
  60. //AddRef
  61. STDMETHODIMP_(ULONG) CDIDeviceActionConfigPage::AddRef()
  62. {
  63.     return InterlockedIncrement(&m_cRef);
  64. }
  65.  
  66.  
  67. //Release
  68. STDMETHODIMP_(ULONG) CDIDeviceActionConfigPage::Release()
  69. {
  70.  
  71.     if (InterlockedDecrement(&m_cRef) == 0)
  72.     {
  73.         delete this;
  74.         return 0;
  75.     }
  76.  
  77.     return m_cRef;
  78. }
  79.  
  80.  
  81. //constructor
  82. CDIDeviceActionConfigPage::CDIDeviceActionConfigPage() :
  83.     m_pDeviceUI(NULL), m_puig(NULL), m_pUIFrame(NULL),
  84.     m_cRef(1), m_lpDiac(NULL), m_State(CFGSTATE_NORMAL),
  85.     m_pCurControl(NULL),
  86.     m_tszIBText(NULL), m_pbmIB(NULL), m_pbmIB2(NULL),
  87.     m_pbmRelAxesGlyph(NULL), m_pbmAbsAxesGlyph(NULL), m_pbmButtonGlyph(NULL),
  88.     m_pbmHatGlyph(NULL), m_pbmCheckGlyph(NULL), m_pbmCheckGlyphDark(NULL),
  89.     m_pRelAxesParent(NULL), m_pAbsAxesParent(NULL), m_pButtonParent(NULL),
  90.     m_pHatParent(NULL),    m_pUnknownParent(NULL),
  91.     m_bFirstDeviceData(TRUE), m_cbDeviceDataSize(0), m_nOnDeviceData(0),
  92.     m_dwLastControlType(0),
  93.     m_nPageIndex(-1)
  94. {
  95.     tracescope(__ts, _T("CDIDeviceActionConfigPage::CDIDeviceActionConfigPage()\n"));
  96.     m_pDeviceData[0] = NULL;
  97.     m_pDeviceData[1] = NULL;
  98. }
  99.  
  100.  
  101. //destructor
  102. CDIDeviceActionConfigPage::~CDIDeviceActionConfigPage()
  103. {
  104.     tracescope(__ts, _T("CDIDeviceActionConfigPage::~CDIDeviceActionConfigPage()\n"));
  105.     if (m_hWnd != NULL)
  106.         Destroy();
  107.  
  108.     FreeResources();
  109.  
  110.     delete m_pDeviceUI;
  111.  
  112.     for (int c = 0; c < 2; c++)
  113.         if (m_pDeviceData[c] != NULL)
  114.             free(m_pDeviceData[c]);
  115.  
  116.     if (m_lpDID != NULL)
  117.     {
  118.         m_lpDID->Unacquire();
  119.         m_lpDID->Release();
  120.     }
  121.     m_lpDID = NULL;
  122. }
  123.  
  124.  
  125. STDMETHODIMP CDIDeviceActionConfigPage::Create(DICFGPAGECREATESTRUCT *pcs)
  126. {
  127.     tracescope(__ts, _T("CDIDeviceActionConfigPage::Create()\n"));
  128.     if (pcs == NULL)
  129.         return E_INVALIDARG;
  130.     DICFGPAGECREATESTRUCT &cs = *pcs;
  131.     
  132.     // validate/save uig and uif
  133.     m_puig = pcs->pUIGlobals;
  134.     m_pUIFrame = pcs->pUIFrame;
  135.     if (m_puig == NULL || m_pUIFrame == NULL)
  136.         return E_INVALIDARG;
  137.  
  138.     // save page index
  139.     m_nPageIndex = pcs->nPage;
  140.     assert(m_nPageIndex >= 0);
  141.  
  142.     // create deviceui with uig, or fail
  143.     m_pDeviceUI = new CDeviceUI(*m_puig, *m_pUIFrame);
  144.     if (m_pDeviceUI == NULL)
  145.         return E_FAIL;
  146.  
  147.     // save the device instance
  148.     m_didi = cs.didi;
  149.     m_lpDID = cs.lpDID;
  150.     if (m_lpDID != NULL)
  151.         m_lpDID->AddRef();
  152.  
  153.     // create the window
  154.     HWND hWnd = NULL;
  155.     assert(m_puig != NULL);
  156.     RECT rect = {0, 0, 1, 1};
  157.     hWnd = CFlexWnd::Create(cs.hParentWnd, rect, FALSE);
  158.  
  159.     // return the handle
  160.     cs.hPageWnd = hWnd;
  161.  
  162.     assert(m_puig != NULL);
  163.  
  164.     // Create the information box
  165.     m_InfoBox.Create(m_hWnd, g_InfoWndRect, TRUE);
  166.     m_InfoBox.SetFont((HFONT)m_puig->GetFont(UIE_USERNAMES));
  167.     m_InfoBox.SetColors(m_puig->GetTextColor(UIE_USERNAMES),
  168.                         m_puig->GetBkColor(UIE_USERNAMES),
  169.                         m_puig->GetTextColor(UIE_USERNAMESEL),
  170.                         m_puig->GetBkColor(UIE_USERNAMESEL),
  171.                         m_puig->GetBrushColor(UIE_USERNAMES),
  172.                         m_puig->GetPenColor(UIE_USERNAMES));
  173.     SetAppropriateDefaultText();
  174.  
  175.     // Create the check box only if this is a keyboard device.
  176.     if (LOBYTE(LOWORD(m_didi.dwDevType)) == DI8DEVTYPE_KEYBOARD)
  177.     {
  178.         m_CheckBox.Create(m_hWnd, g_CheckBoxRect, FALSE);
  179.         m_CheckBox.SetNotify(m_hWnd);
  180.         m_CheckBox.SetFont((HFONT)m_puig->GetFont(UIE_USERNAMES));
  181.         m_CheckBox.SetColors(m_puig->GetTextColor(UIE_USERNAMES),
  182.                              m_puig->GetBkColor(UIE_USERNAMES),
  183.                              m_puig->GetTextColor(UIE_USERNAMESEL),
  184.                              m_puig->GetBkColor(UIE_USERNAMESEL),
  185.                              m_puig->GetBrushColor(UIE_USERNAMES),
  186.                              m_puig->GetPenColor(UIE_USERNAMES));
  187.  
  188.         TCHAR tszResourceString[MAX_PATH];
  189.         LoadString(g_hModule, IDS_SORTASSIGNED, tszResourceString, MAX_PATH);
  190.         m_CheckBox.SetText(tszResourceString);
  191.         m_CheckBox.SetCheck(TRUE);
  192.         ::ShowWindow(m_CheckBox.m_hWnd, SW_SHOW);
  193.     }
  194.  
  195.     // create the username dropdown if necessary
  196.     FLEXCOMBOBOXCREATESTRUCT cbcs;
  197.     cbcs.dwSize = sizeof(FLEXCOMBOBOXCREATESTRUCT);
  198.     cbcs.dwFlags = FCBF_DEFAULT;
  199.     cbcs.dwListBoxFlags = FCBF_DEFAULT|FLBF_INTEGRALHEIGHT;
  200.     cbcs.hWndParent = m_hWnd;
  201.     cbcs.hWndNotify = m_hWnd;
  202.     cbcs.bVisible = TRUE;
  203.     cbcs.rect = g_UserNamesRect;
  204.     cbcs.hFont = (HFONT)m_puig->GetFont(UIE_USERNAMES);
  205.     cbcs.rgbText = m_puig->GetTextColor(UIE_USERNAMES);
  206.     cbcs.rgbBk = m_puig->GetBkColor(UIE_USERNAMES);
  207.     cbcs.rgbSelText = m_puig->GetTextColor(UIE_USERNAMESEL);
  208.     cbcs.rgbSelBk = m_puig->GetBkColor(UIE_USERNAMESEL);
  209.     cbcs.rgbFill = m_puig->GetBrushColor(UIE_USERNAMES);
  210.     cbcs.rgbLine = m_puig->GetPenColor(UIE_USERNAMES);
  211.     cbcs.nSBWidth = 11;
  212.  
  213.     if (m_puig->GetNumUserNames() > 0 && m_hWnd != NULL
  214.        )
  215.     {
  216.         for (int i = 0, n = m_puig->GetNumUserNames(); i < n; i++)
  217.             m_UserNames.AddString(SAFESTR(m_puig->GetUserName(i)));
  218.         m_UserNames.AddString(SAFESTR(_T("(unassigned)")));
  219.  
  220.         m_UserNames.Create(&cbcs);
  221.  
  222.         int nUser = m_pUIFrame->GetCurUser(m_nPageIndex);
  223.         if (nUser == -1)
  224.             nUser = m_puig->GetNumUserNames();
  225.         m_UserNames.SetSel(nUser);
  226.     } else
  227.     if (m_hWnd != NULL)
  228.         m_UserNames.SetSel(0);  // If only 1 user, still must set selection to 0 or we get error later.
  229.  
  230.     // If we are in view mode, set username combobox to read only so user can't change its value.
  231.     if (!m_puig->InEditMode())
  232.         m_UserNames.SetReadOnly(TRUE);
  233.  
  234.     if (m_puig->GetNumMasterAcFors() > 1 && m_hWnd != NULL)
  235.     {
  236.         for (int i = 0, n = m_puig->GetNumMasterAcFors(); i < n; i++)
  237.             m_Genres.AddString(SAFESTR(m_puig->RefMasterAcFor(i).tszActionMap));
  238.  
  239.         cbcs.rect = g_GenresRect;
  240.         m_Genres.Create(&cbcs);
  241.         m_Genres.SetSel(m_pUIFrame->GetCurGenre());
  242.     }
  243.  
  244.     // return success/fail
  245.     return hWnd != NULL ? S_OK : E_FAIL;
  246. }
  247.  
  248. STDMETHODIMP CDIDeviceActionConfigPage::Show(LPDIACTIONFORMATW lpDiActFor)
  249. {
  250.     // save the format pointer
  251.     m_lpDiac = lpDiActFor;
  252.  
  253.     // force tree init
  254.     InitTree(TRUE);
  255.  
  256.     // show the assignments for the controls
  257.     SetControlAssignments();
  258.  
  259.     // show the assignment for the current control
  260.     ShowCurrentControlAssignment();
  261.  
  262.     // Sort the list if check box is checked.
  263.     if (m_CheckBox.GetCheck())
  264.         m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
  265.  
  266.     // show the window
  267.     if (m_hWnd != NULL)
  268.         ShowWindow(m_hWnd, SW_SHOW);
  269.  
  270.     SetFocus(m_hWnd);
  271.  
  272.     return S_OK;
  273. }
  274.  
  275. STDMETHODIMP CDIDeviceActionConfigPage::Hide()
  276. {
  277.     // clear the tree
  278.     ClearTree();
  279.  
  280.     // null the format pointer
  281.     m_lpDiac = NULL;
  282.  
  283.     // hide the window
  284.     if (m_hWnd != NULL)
  285.         ShowWindow(m_hWnd, SW_HIDE);
  286.  
  287.     // If we are in the assign state, exit it.
  288.     if (m_State == CFGSTATE_ASSIGN)
  289.         ExitAssignState();
  290.  
  291.     return S_OK;
  292. }
  293.  
  294. void CDIDeviceActionConfigPage::InitIB()
  295. {
  296.     RECT z = {0,0,0,0};
  297.     SIZE bsize = {0,0};
  298.     m_rectIB = z;
  299.     if (m_pbmIB != NULL)
  300.     {
  301.         if (m_pbmIB->GetSize(&bsize))
  302.         {
  303.             m_rectIB.right = bsize.cx * 2;
  304.             m_rectIB.bottom = bsize.cy;
  305.         }
  306.     }
  307.  
  308.     const int IBORIGINX = 200, IBORIGINY = 394,
  309.         IBTEXTMARGINLEFT = 5;
  310.     POINT ptIBOrigin = {IBORIGINX, IBORIGINY};
  311.  
  312.     m_tszIBText = _T("Click here to see different views of your controller.");
  313.     SIZE tsize = GetTextSize(m_tszIBText, (HFONT)m_puig->GetFont(UIE_VIEWSEL));
  314.     m_ptIBOffset.x = 0;
  315.     m_ptIBOffset.y = 0;
  316.     int tofs = 0;
  317.     if (m_rectIB.bottom < tsize.cy)
  318.     {
  319.         m_rectIB.bottom = tsize.cy;
  320.         m_ptIBOffset.y = (tsize.cy - bsize.cy) / 2;
  321.     }
  322.     else if (tsize.cy < m_rectIB.bottom)
  323.         tofs = (bsize.cy - tsize.cy) / 2;
  324.     m_rectIB.right += tsize.cx;
  325.     if (m_pbmIB != NULL)
  326.         m_rectIB.right += IBTEXTMARGINLEFT * 2;
  327.  
  328.     OffsetRect(&m_rectIB, ptIBOrigin.x, ptIBOrigin.y);
  329.     m_ptIBOffset.x += ptIBOrigin.x;
  330.     m_ptIBOffset.y += ptIBOrigin.y;
  331.  
  332.     m_ptIBOffset2.x = m_rectIB.right - bsize.cx;
  333.     m_ptIBOffset2.y = m_ptIBOffset.y;
  334.  
  335.     m_rectIBText = m_rectIB;
  336.     if (m_pbmIB != NULL)
  337.         m_rectIBText.left += IBTEXTMARGINLEFT + bsize.cx;
  338.     if (m_pbmIB2 != NULL)
  339.         m_rectIBText.right -= IBTEXTMARGINLEFT + bsize.cx;
  340.     m_rectIBText.top += tofs;
  341.  
  342.     // Inialize the two RECTs representing the two arrow bitmaps
  343.     m_rectIBLeft = m_rectIBRight = m_rectIB;
  344.     m_rectIBLeft.right = m_rectIBText.left;
  345.     m_rectIBRight.left = m_rectIBText.right;
  346. }
  347.  
  348. void CDIDeviceActionConfigPage::OnInit()
  349. {
  350.     tracescope(__ts, _T("CDIDeviceActionConfigPage::OnInit()\n"));
  351.     // init resources
  352.     InitResources();
  353.  
  354.     // init IB
  355.     InitIB();
  356.  
  357.     // initialize the device UI
  358.     m_pDeviceUI->Init(m_didi, m_lpDID, m_hWnd, this);
  359.  
  360.     // initialize the device
  361.     InitDevice();
  362.  
  363.     // Start a one-shot timer for click to pick
  364.     if (g_fptimeSetEvent)
  365.         g_fptimeSetEvent(DEVICE_POLLING_INTERVAL, DEVICE_POLLING_INTERVAL,
  366.                          CDIDeviceActionConfigPage::DeviceTimerProc, (DWORD_PTR)m_hWnd, TIME_ONESHOT);
  367.  
  368.     // create the tree
  369.     CAPTIONLOOK cl;
  370.     cl.dwMask = CLMF_TEXTCOLOR | CLMF_FONT | CLMF_LINECOLOR;
  371.     cl.rgbTextColor = m_puig->GetTextColor(UIE_CALLOUT);
  372.     cl.rgbLineColor = m_puig->GetPenColor(UIE_BORDER);
  373.     cl.hFont = (HFONT)m_puig->GetFont(UIE_ACTION);
  374.     m_Tree.SetDefCaptionLook(cl);
  375.     cl.rgbTextColor = m_puig->GetTextColor(UIE_CALLOUTHIGH);
  376.     m_Tree.SetDefCaptionLook(cl, TRUE);
  377.     m_Tree.SetBkColor(RGB(0,0,0));
  378.     if (m_puig->InEditMode())
  379.     {
  380.         m_Tree.Create(m_hWnd, g_TreeRect, TRUE, TRUE);
  381.         m_Tree.SetScrollBarColors(
  382.             m_puig->GetBrushColor(UIE_SBTRACK),
  383.             m_puig->GetBrushColor(UIE_SBTHUMB),
  384.             m_puig->GetPenColor(UIE_SBBUTTON));
  385.     }
  386. }
  387.  
  388. void CDIDeviceActionConfigPage::InitResources()
  389. {
  390.     // create glyphs
  391.     if (!m_pbmRelAxesGlyph)
  392.         m_pbmRelAxesGlyph = CBitmap::CreateFromResource(g_hModule, IDB_AXESGLYPH);
  393.     if (!m_pbmAbsAxesGlyph)
  394.         m_pbmAbsAxesGlyph = CBitmap::CreateFromResource(g_hModule, IDB_AXESGLYPH);
  395.     if (!m_pbmButtonGlyph)
  396.         m_pbmButtonGlyph = CBitmap::CreateFromResource(g_hModule, IDB_BUTTONGLYPH);
  397.     if (!m_pbmHatGlyph)
  398.         m_pbmHatGlyph = CBitmap::CreateFromResource(g_hModule, IDB_HATGLYPH);
  399.     if (!m_pbmCheckGlyph)
  400.         m_pbmCheckGlyph = CBitmap::CreateFromResource(g_hModule, IDB_CHECKGLYPH);
  401.     if (!m_pbmCheckGlyphDark)
  402.         m_pbmCheckGlyphDark = CBitmap::CreateFromResource(g_hModule, IDB_CHECKGLYPHDARK);
  403.  
  404.     // create IB bitmaps
  405.     if (!m_pbmIB)
  406.         m_pbmIB = CBitmap::CreateFromResource(g_hModule, IDB_IB);
  407.     if (!m_pbmIB2)
  408.         m_pbmIB2 = CBitmap::CreateFromResource(g_hModule, IDB_IB2);
  409. }
  410.  
  411. void CDIDeviceActionConfigPage::FreeResources()
  412. {
  413.     if (m_pbmRelAxesGlyph)
  414.         delete m_pbmRelAxesGlyph;
  415.     if (m_pbmAbsAxesGlyph)
  416.         delete m_pbmAbsAxesGlyph;
  417.     if (m_pbmButtonGlyph)
  418.         delete m_pbmButtonGlyph;
  419.     if (m_pbmHatGlyph)
  420.         delete m_pbmHatGlyph;
  421.     if (m_pbmCheckGlyph)
  422.         delete m_pbmCheckGlyph;
  423.     if (m_pbmCheckGlyphDark)
  424.         delete m_pbmCheckGlyphDark;
  425.     if (m_pbmIB)
  426.         delete m_pbmIB;
  427.     if (m_pbmIB2)
  428.         delete m_pbmIB2;
  429.     m_pbmRelAxesGlyph = NULL;
  430.     m_pbmAbsAxesGlyph = NULL;
  431.     m_pbmButtonGlyph = NULL;
  432.     m_pbmHatGlyph = NULL;
  433.     m_pbmCheckGlyph = NULL;
  434.     m_pbmCheckGlyphDark = NULL;
  435.     m_pbmIB = NULL;
  436.     m_pbmIB2 = NULL;
  437. }
  438.  
  439. void CDIDeviceActionConfigPage::ClearTree()
  440. {
  441.     m_Tree.FreeAll();
  442.     m_pRelAxesParent = NULL;
  443.     m_pAbsAxesParent = NULL;
  444.     m_pButtonParent = NULL;
  445.     m_pHatParent = NULL;
  446.     m_pUnknownParent = NULL;
  447.     m_dwLastControlType = 0;
  448. }
  449.  
  450. void CDIDeviceActionConfigPage::InitTree(BOOL bForceInit)
  451. {
  452.     // get type of control
  453.     DWORD dwControlType = 0;
  454.     if (m_pCurControl && m_pCurControl->IsOffsetAssigned())
  455.     {
  456.         DWORD dwObjId = m_pCurControl->GetOffset();
  457.  
  458.         if (dwObjId & DIDFT_RELAXIS)
  459.             dwControlType = DIDFT_RELAXIS;
  460.         else if (dwObjId & DIDFT_ABSAXIS)
  461.             dwControlType = DIDFT_ABSAXIS;
  462.         else if (dwObjId & DIDFT_BUTTON)
  463.             dwControlType = DIDFT_BUTTON;
  464.         else if (dwObjId & DIDFT_POV)
  465.             dwControlType = DIDFT_POV;
  466.     }
  467.  
  468.     // Turn off the tree's readonly flag if we are in the assign state.
  469.     // We will turn it on later if current control's action has DIA_APPFIXED.
  470.     if (m_State == CFGSTATE_NORMAL)
  471.         m_Tree.SetReadOnly(TRUE);
  472.     else
  473.         m_Tree.SetReadOnly(FALSE);
  474.  
  475.     // if this control type is the same as the last, do nothing,
  476.     // unless we're force init
  477.     if (m_dwLastControlType == dwControlType && !bForceInit && m_State)
  478.         return;
  479.  
  480.     // delete the whole tree
  481.     ClearTree();
  482.  
  483.     // can't use tree if there is no diac or action array
  484.     if (m_lpDiac == NULL || m_lpDiac->rgoAction == NULL)
  485.         return;
  486.  
  487.     // also can't use if we don't have a control type
  488.     if (dwControlType == 0)
  489.         return;
  490.  
  491.     // prepare margin rects
  492.     RECT labelmargin = {14, 6, 3, 3};
  493.     RECT itemmargin = {14, 1, 3, 2};
  494.  
  495.     // set default indents
  496.     m_Tree.SetRootChildIndent(5);
  497.     m_Tree.SetDefChildIndent(12);
  498.  
  499.     // add the control type sections
  500.     m_Tree.SetDefMargin(labelmargin);
  501.     TCHAR tszResourceString[MAX_PATH];
  502.     switch (dwControlType)
  503.     {
  504.         case DIDFT_RELAXIS:
  505.             LoadString(g_hModule, IDS_AXISACTIONS, tszResourceString, MAX_PATH);
  506.             m_pRelAxesParent = m_Tree.DefAddItem(tszResourceString);
  507.             break;
  508.  
  509.         case DIDFT_ABSAXIS:
  510.             LoadString(g_hModule, IDS_AXISACTIONS, tszResourceString, MAX_PATH);
  511.             m_pAbsAxesParent = m_Tree.DefAddItem(tszResourceString);
  512.             break;
  513.  
  514.         case DIDFT_BUTTON:
  515.             LoadString(g_hModule, IDS_BUTTONACTIONS, tszResourceString, MAX_PATH);
  516.             m_pButtonParent = m_Tree.DefAddItem(tszResourceString);
  517.             break;
  518.  
  519.         case DIDFT_POV:
  520.             LoadString(g_hModule, IDS_POVACTIONS, tszResourceString, MAX_PATH);
  521.             m_pHatParent = m_Tree.DefAddItem(tszResourceString);
  522.             break;
  523.  
  524.         default:
  525.             return;
  526.     }
  527.  
  528.     // populate the tree
  529.     m_Tree.SetDefMargin(itemmargin);
  530.     for (unsigned int i = 0; i < m_lpDiac->dwNumActions; i++)
  531.     {
  532.         DIACTIONW *pAction = m_lpDiac->rgoAction + i;
  533.         CFTItem *pItem = NULL;
  534.  
  535.         if (pAction == NULL) 
  536.             continue;
  537.  
  538.         switch (pAction->dwSemantic & DISEM_TYPE_MASK)
  539.         {
  540.             case DISEM_TYPE_AXIS:
  541.                 // Must distinguish between relative and absolute
  542.                 switch((pAction->dwSemantic & DISEM_REL_MASK) >> DISEM_REL_SHIFT)
  543.                 {
  544.                     case 0: pItem = m_pAbsAxesParent; break;
  545.                     case 1: pItem = m_pRelAxesParent; break;
  546.                 }
  547.                 break;
  548.             case DISEM_TYPE_BUTTON: pItem = m_pButtonParent; break;
  549.             case DISEM_TYPE_POV: pItem = m_pHatParent; break;
  550.         }
  551.  
  552.         if (pItem == NULL)
  553.             continue;
  554.  
  555.         // Add action with this name
  556.         CFTItem *pAlready = GetItemWithActionNameAndSemType(pAction->lptszActionName, pAction->dwSemantic);
  557.         if (!pAlready)
  558.         {
  559.             LPTSTR acname = AllocLPTSTR(pAction->lptszActionName);
  560.             pItem = m_Tree.DefAddItem(acname, pItem, ATTACH_LASTCHILD);  // This might return NULL.
  561.             free(acname);
  562.             if (pItem)
  563.                 pItem->SetUserData((LPVOID)(new RGLPDIACW));
  564.         }
  565.         else
  566.         {
  567.             pItem = pAlready;
  568.         }
  569.  
  570.         if (pItem == NULL)
  571.             continue;
  572.  
  573.         pItem->SetUserGUID(GUID_ActionItem);
  574.         RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData();
  575.         if (pacs)
  576.             pacs->SetAtGrow(pacs->GetSize(), pAction);
  577.  
  578.         if (pAlready)
  579.         {
  580.             // The tree already has an action with this name.  Check the DIA_APPFIXED flag for each DIACTION
  581.             // that this item holds.
  582.             DWORD dwNumActions = GetNumItemLpacs(pItem);
  583.             for (DWORD i = 0; i < dwNumActions; ++i)
  584.             {
  585.                 LPDIACTIONW lpExistingAc = GetItemLpac(pItem, i);
  586.                 // If the DIACTION that is assigned to this device has DIA_APPFIXED flag, then
  587.                 //   the other must have it too.
  588.                 if (lpExistingAc && IsEqualGUID(lpExistingAc->guidInstance, m_didi.guidInstance))
  589.                 {
  590.                     if (lpExistingAc->dwFlags & DIA_APPFIXED)
  591.                     {
  592.                         // If this DIACTION has DIA_APPFIXED, then all DIACTIONs must have it too.
  593.                         for (DWORD j = 0; j < dwNumActions; ++j)
  594.                         {
  595.                             LPDIACTIONW lpChangeAc = GetItemLpac(pItem, j);
  596.                             if (lpChangeAc)
  597.                                 lpChangeAc->dwFlags |= DIA_APPFIXED;
  598.                         }
  599.                     }
  600.  
  601.                     break;  // Break the loop since we already found the DIACTION that is assigned.
  602.                 }
  603.             }
  604.         }  // if (pAlready)
  605.     }
  606.  
  607.     // show all
  608.     m_Tree.GetRoot()->ExpandAll();
  609.     m_dwLastControlType = dwControlType;
  610. }
  611.  
  612. int CompareActionNames(LPCWSTR acname1, LPCWSTR acname2)
  613. {
  614. #ifdef CFGUI__COMPAREACTIONNAMES_CASE_INSENSITIVE
  615.     return _wcsicmp(acname1, acname2);
  616. #else
  617.     return wcscmp(acname1, acname2);
  618. #endif
  619. }
  620.  
  621. CFTItem *CDIDeviceActionConfigPage::GetItemWithActionNameAndSemType(LPCWSTR acname, DWORD dwSemantic)
  622. {
  623.     CFTItem *pItem = m_Tree.GetFirstItem();
  624.     for (; pItem != NULL; pItem = pItem->GetNext())
  625.     {
  626.         if (!pItem->IsUserGUID(GUID_ActionItem))
  627.             continue;
  628.         
  629.         LPDIACTIONW lpac = GetItemLpac(pItem);
  630.         if (!lpac)
  631.             continue;
  632.  
  633.         // Check semantic type
  634.         if ((lpac->dwSemantic & DISEM_TYPE_MASK) != (dwSemantic & DISEM_TYPE_MASK))
  635.             continue;
  636.  
  637.         // If both are axis, check for relative/absolute
  638.         if ((lpac->dwSemantic & DISEM_TYPE_MASK) == DISEM_TYPE_AXIS)
  639.             if ((lpac->dwSemantic & DISEM_REL_MASK) != (dwSemantic & DISEM_REL_MASK))
  640.                 continue;
  641.  
  642.         // Check name
  643.         if (CompareActionNames(lpac->lptszActionName, acname) == 0)
  644.             return pItem;
  645.     }
  646.  
  647.     return NULL;
  648. }
  649.  
  650. void CDIDeviceActionConfigPage::OnPaint(HDC hDC)
  651. {
  652.     TCHAR tszResourceString[MAX_PATH];
  653.     CPaintHelper ph(*m_puig, hDC);
  654.  
  655.     ph.SetBrush(UIB_BLACK);
  656.     RECT rect;
  657.     GetClientRect(&rect);
  658.     ph.Rectangle(rect, UIR_SOLID);
  659.  
  660.     ph.SetText(UIC_BORDER, UIC_BLACK);
  661.     {
  662.         rect = g_UserNamesTitleRect;
  663.         LoadString(g_hModule, IDS_PLAYER_TITLE, tszResourceString, MAX_PATH);
  664.         DrawText(hDC, tszResourceString, -1, &rect, DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
  665.     }
  666.     if (m_puig->GetNumMasterAcFors() > 1)
  667.     {
  668.         rect = g_GenresTitleRect;
  669.         LoadString(g_hModule, IDS_GENRE_TITLE, tszResourceString, MAX_PATH);
  670.         DrawText(hDC, tszResourceString, -1, &rect, DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
  671.     }
  672.  
  673.     // Draw tree window title and outline if we are in edit mode.
  674.     if (m_puig->InEditMode())
  675.     {
  676.         COLORREF BorderColor = m_puig->GetColor(UIC_BORDER);
  677.         if (m_Tree.GetReadOnly())
  678.             BorderColor = RGB(GetRValue(BorderColor)>>1, GetGValue(BorderColor)>>1, GetBValue(BorderColor)>>1);
  679.  
  680.         ::SetTextColor(hDC, BorderColor);  // Use the muted color if tree is read only.
  681.         // Draw tree window title (Available Actions)
  682.         rect = g_TreeTitleRect;
  683.         LoadString(g_hModule, IDS_AVAILABLEACTIONS_TITLE, tszResourceString, MAX_PATH);
  684.         DrawText(hDC, tszResourceString, -1, &rect, DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
  685.         // Draw tree window outline
  686.         HGDIOBJ hPen, hOldPen;
  687.         if (m_Tree.GetReadOnly())
  688.         {
  689.             hPen = CreatePen(PS_SOLID, 0, BorderColor);
  690.             hOldPen = ::SelectObject(hDC, hPen);
  691.         }
  692.         else
  693.             ph.SetPen(UIP_BORDER);
  694.  
  695.         RECT rc = g_TreeRect;
  696.         InflateRect(&rc, 1, 1);
  697.         Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
  698.         if (m_Tree.GetReadOnly())
  699.         {
  700.             ::SelectObject(hDC, hOldPen);
  701.             DeleteObject(hPen);
  702.         }
  703.     }
  704.  
  705.     if (m_pDeviceUI->GetNumViews() < 2)
  706.         return;
  707.  
  708.     if (m_pbmIB != NULL)
  709.         m_pbmIB->Draw(hDC, m_ptIBOffset);
  710.     if (m_pbmIB2 != NULL)
  711.         m_pbmIB2->Draw(hDC, m_ptIBOffset2);
  712.     if (m_tszIBText != NULL)
  713.     {
  714.         ph.SetElement(UIE_VIEWSEL);
  715.         RECT rect = m_rectIBText;
  716.         DrawText(hDC, m_tszIBText, -1, &rect, DT_NOCLIP | DT_NOPREFIX);
  717.     }
  718. }
  719.  
  720. void CDIDeviceActionConfigPage::SetCurrentControl(CDeviceControl *pControl)
  721. {
  722.     // If the new control is the same as the old, no need to do anything.
  723.     if (m_pCurControl == pControl)
  724.         return;
  725.     if (m_pCurControl != NULL)
  726.     {
  727.         m_pCurControl->Unhighlight();
  728.         // If we don't have a current control, then invalidate the view so that the old callout can be repainted.
  729.         // If there is a current control, the view will be invalidated by Highlight().
  730.         if (!pControl)
  731.             m_pCurControl->Invalidate();
  732.     }
  733.     m_pCurControl = pControl;
  734.     if (m_pCurControl != NULL)
  735.         m_pCurControl->Highlight();
  736.     ShowCurrentControlAssignment();
  737. }
  738.  
  739. CFTItem *CDIDeviceActionConfigPage::GetItemForActionAssignedToControl(CDeviceControl *pControl)
  740. {
  741.     if (!pControl)
  742.         return NULL;
  743.  
  744.     // find the item for the action assigned to this control, if any
  745.     CFTItem *pItem = m_Tree.GetFirstItem();
  746.     for (; pItem != NULL; pItem = pItem->GetNext())
  747.     {
  748.         if (!pItem->IsUserGUID(GUID_ActionItem))
  749.             continue;
  750.  
  751.         for (int i = 0, n = GetNumItemLpacs(pItem); i < n; i++)
  752.         {
  753.             LPDIACTIONW lpac = GetItemLpac(pItem, i);
  754.             if (!lpac)
  755.                 continue;
  756.  
  757.             if (IsEqualGUID(lpac->guidInstance, m_didi.guidInstance) &&
  758.                     GetOffset(lpac) == pControl->GetOffset())
  759.                 return pItem;
  760.         }
  761.     }
  762.  
  763.     return NULL;
  764. }
  765.  
  766. int CDIDeviceActionConfigPage::GetNumItemLpacs(CFTItem *pItem)
  767. {
  768.     if (pItem == NULL)
  769.         return 0;
  770.  
  771.     RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData();
  772.     if (!pacs)
  773.         return 0;
  774.     else
  775.         return pacs->GetSize();
  776. }
  777.  
  778. LPDIACTIONW CDIDeviceActionConfigPage::GetItemLpac(CFTItem *pItem, int i)
  779. {
  780.     if (pItem == NULL)
  781.         return NULL;
  782.  
  783.     RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData();
  784.     if (!pacs || i < 0 || i >= pacs->GetSize())
  785.         return NULL;
  786.     else    
  787.         return pacs->GetAt(i);
  788. }
  789.  
  790. void CDIDeviceActionConfigPage::ShowCurrentControlAssignment()
  791. {
  792.     // init the tree
  793.     InitTree();
  794.  
  795.     // if we don't have a control...
  796.     if (m_pCurControl == NULL)
  797.     {
  798.         // select nothing
  799.         m_Tree.SetCurSel(NULL);
  800.         return;
  801.     }
  802.  
  803.     // find the item for the action assigned to this control, if any
  804.     CFTItem *pItem = GetItemForActionAssignedToControl(m_pCurControl);
  805.  
  806.     // if we didn't find a match...
  807.     if (!pItem)
  808.     {
  809.         // select nothing
  810.         m_Tree.SetCurSel(NULL);
  811.         return;
  812.     }
  813.  
  814.     // We need to check if the action this control is assigned to has DIA_APPFIXED flag.
  815.     // If it does, this control cannot be remapped to another action.
  816.     // We prevent this by setting the tree control to read-only, so it can't receive any clicks.
  817.     LPDIACTIONW lpAc = GetItemLpac(pItem);  // Get the action
  818.     if (lpAc && (lpAc->dwFlags & DIA_APPFIXED))
  819.         m_Tree.SetReadOnly(TRUE);
  820.  
  821.     // otherwise, show item and select it
  822.     pItem->EnsureVisible();
  823.     m_Tree.SetCurSel(pItem);
  824. }
  825.  
  826. void CDIDeviceActionConfigPage::DeviceUINotify(const DEVICEUINOTIFY &uin)
  827. {
  828.     switch (uin.msg)
  829.     {
  830.         case DEVUINM_NUMVIEWSCHANGED:
  831.             Invalidate();
  832.             break;
  833.  
  834.         case DEVUINM_SELVIEW:
  835.             // set the view
  836.             m_pDeviceUI->SetView(uin.selview.nView);
  837.  
  838.             // show the assignments for the controls
  839.             SetControlAssignments();
  840.  
  841.             // select nothing
  842.             SetCurrentControl(NULL);
  843.             break;
  844.  
  845.         case DEVUINM_ONCONTROLDESTROY:
  846.             if (uin.control.pControl == m_pCurControl)
  847.                 m_pCurControl = NULL;
  848.             break;
  849.  
  850.         case DEVUINM_CLICK:
  851.             ExitAssignState();
  852.             switch (uin.from)
  853.             {
  854.                 case DEVUINFROM_CONTROL:
  855.                     SetCurrentControl(uin.control.pControl);
  856.                     SetAppropriateDefaultText();
  857.                     break;
  858.                 case DEVUINFROM_VIEWWND:
  859.                     break;
  860.             }
  861.             break;
  862.  
  863.         case DEVUINM_DOUBLECLICK:
  864.             switch (uin.from)
  865.             {
  866.                 case DEVUINFROM_CONTROL:
  867.                     EnterAssignState();
  868.                     break;
  869.             }
  870.             break;
  871.  
  872.         case DEVUINM_MOUSEOVER:
  873.             SetAppropriateDefaultText();
  874.             break;
  875.  
  876.         case DEVUINM_RENEWDEVICE:
  877.             HWND hParent = GetParent(m_hWnd);
  878.             CConfigWnd *pCfgWnd = (CConfigWnd *)GetFlexWnd(hParent);
  879.             if (pCfgWnd)
  880.             {
  881.                 LPDIRECTINPUTDEVICE8W lpDID = pCfgWnd->RenewDevice(m_didi.guidInstance);
  882.                 if (lpDID)
  883.                 {
  884.                     // Destroy the device instance we have
  885.                     if (m_lpDID) m_lpDID->Release();
  886.                     lpDID->AddRef();
  887.                     m_lpDID = lpDID;
  888.                 }
  889.                 m_pDeviceUI->SetDevice(lpDID);  // Sets the device pointer in CDeviceUI (no need to AddRef)
  890.             }
  891.     }
  892. }
  893.  
  894. void CDIDeviceActionConfigPage::UnassignCallout()
  895. {
  896.     // find the item for the action assigned to this control, if any
  897.     CFTItem *pItem = GetItemForActionAssignedToControl(m_pCurControl);
  898.     if (pItem)
  899.     {
  900.         LPDIACTIONW lpac = GetItemLpac(pItem);
  901.         // Only unassign if the action doesn't have DIA_APPFIXED flag.
  902.         if (lpac && !(lpac->dwFlags & DIA_APPFIXED))
  903.         {
  904.             ActionClick(NULL);
  905.             m_Tree.Invalidate();
  906.         }
  907.     }
  908.     // Sort the list if the check box is checked.
  909.     if (m_CheckBox.GetCheck())
  910.         m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
  911. }
  912.  
  913. void CDIDeviceActionConfigPage::NullAction(LPDIACTIONW lpac)
  914. {
  915.     if (lpac == NULL)
  916.         return;
  917.  
  918.     SetInvalid(lpac);
  919.  
  920. }
  921.  
  922. void CDIDeviceActionConfigPage::UnassignActionsAssignedTo(const GUID &guidInstance, DWORD dwOffset)
  923. {
  924.     if (m_lpDiac == NULL || m_lpDiac->rgoAction == NULL)
  925.         return;
  926.  
  927.     if (IsEqualGUID(guidInstance, GUID_NULL))
  928.         return;
  929.  
  930.     // assign any actions assigned to this control to nothing
  931.     DWORD i;
  932.     LPDIACTIONW lpac;
  933.     for (i = 0, lpac = m_lpDiac->rgoAction; i < m_lpDiac->dwNumActions; i++, lpac++)
  934.         if (IsEqualGUID(guidInstance, lpac->guidInstance) && dwOffset == GetOffset(lpac)/*->dwInternalOffset*/)
  935.         {
  936.             GlobalUnassignControlAt(guidInstance, dwOffset);
  937.             NullAction(lpac);
  938.         }
  939. }
  940.  
  941. void CDIDeviceActionConfigPage::UnassignControl(CDeviceControl *pControl)
  942. {
  943.     if (pControl == NULL)
  944.         return;
  945.  
  946.     // make sure the control itself indicates unassignment
  947.     pControl->SetCaption(g_tszUnassignedControlCaption);
  948. }
  949.  
  950. void CallUnassignControl(CDeviceControl *pControl, LPVOID pVoid, BOOL bFixed)
  951. {
  952.     CDIDeviceActionConfigPage *pThis = (CDIDeviceActionConfigPage *)pVoid;
  953.     pThis->UnassignControl(pControl);
  954. }
  955.  
  956. void CDIDeviceActionConfigPage::GlobalUnassignControlAt(const GUID &guidInstance, DWORD dwOffset)
  957. {
  958.     if (IsEqualGUID(guidInstance, GUID_NULL))
  959.         return;
  960.  
  961.     if (IsEqualGUID(guidInstance, m_didi.guidInstance))
  962.         m_pDeviceUI->DoForAllControlsAtOffset(dwOffset, CallUnassignControl, this);
  963. }
  964.  
  965. // this function must find whatever control is assigned to this action and unassign it
  966. void CDIDeviceActionConfigPage::UnassignAction(LPDIACTIONW slpac)
  967. {
  968.     // call UnassignSpecificAction for each action with the same name
  969.     // as this one, including this one
  970.     
  971.     if (slpac == NULL)
  972.         return;
  973.  
  974.     CFTItem *pItem = GetItemWithActionNameAndSemType(slpac->lptszActionName, slpac->dwSemantic);
  975.     if (!pItem)
  976.         return;
  977.  
  978.     RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData();
  979.     if (!pacs)
  980.         return;
  981.  
  982.     for (int i = 0; i < pacs->GetSize(); i++)
  983.         UnassignSpecificAction(pacs->GetAt(i));
  984. }
  985.  
  986. void CDIDeviceActionConfigPage::UnassignSpecificAction(LPDIACTIONW lpac)
  987. {
  988.     if (lpac == NULL)
  989.         return;
  990.  
  991.     if (IsEqualGUID(lpac->guidInstance, GUID_NULL))
  992.         return;
  993.  
  994.     // if there's a control with this instance/offset, unassign it
  995.     UnassignActionsAssignedTo(lpac->guidInstance, GetOffset(lpac)/*->dwInternalOffset*/);
  996.     GlobalUnassignControlAt(lpac->guidInstance, GetOffset(lpac)/*->dwInternalOffset*/);
  997.  
  998.     // now actually null the action
  999.     NullAction(lpac);
  1000. }
  1001.  
  1002. void CDIDeviceActionConfigPage::AssignCurrentControlToAction(LPDIACTIONW lpac)
  1003. {
  1004.     // if there is a control, unassign it
  1005.     if (m_pCurControl != NULL)
  1006.     {
  1007.         UnassignControl(m_pCurControl);
  1008.         GUID guidInstance;
  1009.         DWORD dwOffset;
  1010.         m_pCurControl->GetInfo(guidInstance, dwOffset);
  1011.         UnassignActionsAssignedTo(guidInstance, dwOffset);
  1012.     }
  1013.  
  1014.     // if there is an action, unassign it
  1015.     if (lpac != NULL)
  1016.         UnassignAction(lpac);
  1017.  
  1018.     // can only continue if we have both
  1019.     if (lpac == NULL || m_pCurControl == NULL)
  1020.         return;
  1021.  
  1022.     // here we should have a control and an action
  1023.     assert(lpac != NULL);
  1024.     assert(m_pCurControl != NULL);
  1025.  
  1026.     // because an action can only be assigned to one control,
  1027.     // make sure this action is unassigned first
  1028.     UnassignAction(lpac);
  1029.  
  1030.     // now actually assign
  1031.     DWORD ofs;
  1032.     m_pCurControl->GetInfo(lpac->guidInstance, ofs/*lpac->dwInternalOffset*/);
  1033.     SetOffset(lpac, ofs);
  1034.     LPTSTR acname = AllocLPTSTR(lpac->lptszActionName);
  1035.     m_pCurControl->SetCaption(acname, lpac->dwFlags & DIA_APPFIXED);
  1036.     free(acname);
  1037.  
  1038.     // Sort the action list if check box is checked
  1039.     if (m_CheckBox.GetCheck())
  1040.     {
  1041.         m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
  1042.         // Scroll so that we scroll to make this visible since it might be displaced by sorting.
  1043.         m_pDeviceUI->GetCurView()->ScrollToMakeControlVisible(m_pCurControl->GetCalloutMaxRect());
  1044.     }
  1045. }
  1046.  
  1047. void CDIDeviceActionConfigPage::ActionClick(LPDIACTIONW lpac)
  1048. {
  1049.     if (m_pCurControl != NULL)
  1050.         AssignCurrentControlToAction(lpac);
  1051.     // Change the state back to normal
  1052.     ExitAssignState();
  1053. }
  1054.  
  1055. void CDIDeviceActionConfigPage::SetControlAssignments()
  1056. {
  1057.     assert(!IsEqualGUID(m_didi.guidInstance, GUID_NULL));
  1058.  
  1059.     m_pDeviceUI->SetAllControlCaptionsTo(g_tszUnassignedControlCaption);
  1060.  
  1061.     if (m_lpDiac == NULL || m_lpDiac->rgoAction == NULL)
  1062.         return;
  1063.  
  1064.     DWORD i;
  1065.     LPDIACTIONW lpac;
  1066.     for (i = 0, lpac = m_lpDiac->rgoAction; i < m_lpDiac->dwNumActions; i++)
  1067.     {
  1068.         lpac = m_lpDiac->rgoAction + i;
  1069.  
  1070.         if (IsEqualGUID(lpac->guidInstance, GUID_NULL))
  1071.             continue;
  1072.  
  1073.         if (!IsEqualGUID(lpac->guidInstance, m_didi.guidInstance))
  1074.             continue;
  1075.  
  1076.         LPTSTR acname = AllocLPTSTR(lpac->lptszActionName);
  1077.         m_pDeviceUI->SetCaptionForControlsAtOffset(GetOffset(lpac)/*->dwInternalOffset*/, acname, lpac->dwFlags & DIA_APPFIXED);
  1078.         free(acname);
  1079.     }
  1080. }
  1081.  
  1082. void CDIDeviceActionConfigPage::DoViewSel()
  1083. {
  1084.     m_ViewSelWnd.Go(m_hWnd, m_rectIB.left, m_rectIB.top, m_pDeviceUI);
  1085. }
  1086.  
  1087. void CDIDeviceActionConfigPage::OnClick(POINT point, WPARAM, BOOL bLeft)
  1088. {
  1089.     if (!bLeft)
  1090.         return;
  1091.  
  1092.     if (m_pDeviceUI->GetNumViews() > 1)
  1093.     {
  1094.         int iCurView = m_pDeviceUI->GetCurViewIndex();
  1095.  
  1096.         if (PtInRect(&m_rectIBLeft, point))
  1097.             m_pDeviceUI->SetView(iCurView == 0 ? m_pDeviceUI->GetNumViews() - 1 : iCurView - 1);
  1098.         if (PtInRect(&m_rectIBRight, point))
  1099.             m_pDeviceUI->SetView(iCurView == m_pDeviceUI->GetNumViews() - 1 ? 0 : iCurView + 1);
  1100.         if (PtInRect(&m_rectIBText, point))
  1101.             DoViewSel();
  1102.     }
  1103. }
  1104.  
  1105. void CDIDeviceActionConfigPage::OnMouseOver(POINT point, WPARAM fwKeys)
  1106. {
  1107.     CFlexWnd::s_ToolTip.SetEnable(FALSE);
  1108.  
  1109.     // Check view selection area so we can display text in info box.
  1110.     if (m_pDeviceUI->GetNumViews() > 1)
  1111.     {
  1112.         if (PtInRect(&m_rectIB, point))
  1113.         {
  1114.             SetInfoText(IDS_INFOMSG_VIEW_VIEWSEL);
  1115.             return;
  1116.         }
  1117.     }
  1118.  
  1119.     SetAppropriateDefaultText();
  1120. }
  1121.  
  1122. int GetActionIndexFromPointer(LPDIACTIONW p, LPDIACTIONFORMATW paf)
  1123. {
  1124.     if (!p || !paf || !paf->rgoAction)
  1125.         return -1;
  1126.  
  1127.     int index = int((((LPBYTE)p) - ((LPBYTE)paf->rgoAction)) / (DWORD)sizeof(DIACTIONW));
  1128.  
  1129.     assert(&(paf->rgoAction[index]) == p);
  1130.  
  1131.     return index;
  1132. }
  1133.  
  1134. BOOL CDIDeviceActionConfigPage::IsActionAssignedHere(int index)
  1135. {
  1136.     if (!m_lpDiac)
  1137.         return FALSE;
  1138.  
  1139.     if (index < 0 || index >= (int)m_lpDiac->dwNumActions)
  1140.         return FALSE;
  1141.  
  1142.     return IsEqualGUID(m_didi.guidInstance, m_lpDiac->rgoAction[index].guidInstance);
  1143. }
  1144.  
  1145. LRESULT CDIDeviceActionConfigPage::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1146. {
  1147.     switch (msg)
  1148.     {
  1149.         case WM_KEYDOWN:
  1150. #ifdef DBG
  1151.             // In debug version, shift-escape exits the UI.
  1152.             if (wParam == VK_ESCAPE && GetAsyncKeyState(VK_SHIFT) < 0)
  1153.             {
  1154.                 PostMessage(GetParent(m_hWnd), WM_KEYDOWN, wParam, lParam);
  1155.                 break;
  1156.             }
  1157. #endif
  1158.             // If this is a keyboard device, then click-to-pick will take care of the functionalities below.
  1159.             // Process WM_KEYDOWN only for non-keyboard devices.
  1160.             if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_KEYBOARD) return 0;
  1161.             switch(wParam)
  1162.             {
  1163.                 case VK_RETURN:
  1164.                     // If we are not in assign state, enter it.
  1165.                     if (m_State == CFGSTATE_NORMAL && m_pCurControl)
  1166.                         EnterAssignState();
  1167.                     break;
  1168.  
  1169.                 case VK_DELETE:
  1170.                     // If we are in assign state and there is a control, unassign it.
  1171.                     if (m_State == CFGSTATE_ASSIGN && m_pCurControl)
  1172.                         UnassignCallout();
  1173.                     break;
  1174.  
  1175.                 case VK_ESCAPE:
  1176.                     if (m_State == CFGSTATE_ASSIGN)
  1177.                         ExitAssignState();
  1178.  
  1179.                     break;
  1180.             }
  1181.             return 0;
  1182.  
  1183.         case WM_FLEXCHECKBOX:
  1184.             switch(wParam)
  1185.             {
  1186.                 case CHKNOTIFY_UNCHECK:
  1187.                         m_pDeviceUI->GetCurView()->SortAssigned(FALSE);
  1188.                         if (m_pCurControl)
  1189.                         {
  1190.                             // Scroll so that we scroll to make this visible since it might be displaced by sorting.
  1191.                             m_pDeviceUI->GetCurView()->ScrollToMakeControlVisible(m_pCurControl->GetCalloutMaxRect());
  1192.                         }
  1193.                         Invalidate();
  1194.                     break;
  1195.                 case CHKNOTIFY_CHECK:
  1196.                         m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
  1197.                         if (m_pCurControl)
  1198.                         {
  1199.                             // Scroll so that we scroll to make this visible since it might be displaced by sorting.
  1200.                             m_pDeviceUI->GetCurView()->ScrollToMakeControlVisible(m_pCurControl->GetCalloutMaxRect());
  1201.                         }
  1202.                         Invalidate();
  1203.                     break;
  1204.                 case CHKNOTIFY_MOUSEOVER:
  1205.                     SetInfoText(m_CheckBox.GetCheck() ? IDS_INFOMSG_VIEW_SORTENABLED : IDS_INFOMSG_VIEW_SORTDISABLED);
  1206.                     break;
  1207.             }
  1208.             break;
  1209.  
  1210.         case WM_FLEXCOMBOBOX:
  1211.             switch (wParam)
  1212.             {
  1213.                 case FCBN_MOUSEOVER:
  1214.                     if (lParam)
  1215.                     {
  1216.                         CFlexComboBox *pCombo = (CFlexComboBox*)lParam;
  1217.                         if (pCombo->m_hWnd == m_UserNames.m_hWnd)
  1218.                             SetInfoText(m_puig->InEditMode() ? IDS_INFOMSG_EDIT_USERNAME : IDS_INFOMSG_VIEW_USERNAME);
  1219.                         else if (pCombo->m_hWnd == m_Genres.m_hWnd)
  1220.                             SetInfoText(m_puig->InEditMode() ? IDS_INFOMSG_EDIT_GAMEMODE : IDS_INFOMSG_VIEW_GAMEMODE);
  1221.                     }
  1222.                     break;
  1223.  
  1224.                 case FCBN_SELCHANGE:
  1225.                     // Clear the tool tip as the combo-box has closed
  1226.                     CFlexWnd::s_ToolTip.SetEnable(FALSE);
  1227.                     CFlexWnd::s_ToolTip.SetToolTipParent(NULL);
  1228.                     if (m_pUIFrame && m_puig)
  1229.                     {
  1230.                         ExitAssignState();
  1231.                         m_pUIFrame->SetCurGenre(m_Genres.GetSel());
  1232.                         int nUser = m_UserNames.GetSel();
  1233.                         if (m_puig->GetNumUserNames() > 0 && nUser >= m_puig->GetNumUserNames())
  1234.                             nUser = -1;
  1235.                         m_pUIFrame->SetCurUser(m_nPageIndex, nUser);
  1236.                     }
  1237.                     break;
  1238.             }
  1239.             return 0;
  1240.  
  1241.         case WM_FLEXTREENOTIFY:
  1242.         {
  1243.             // Check if this is a mouse over message (just for info box update)
  1244.             if (wParam == FTN_MOUSEOVER)
  1245.             {
  1246.                 SetAppropriateDefaultText();
  1247.                 return FALSE;
  1248.             }
  1249.  
  1250.             if (!lParam)
  1251.                 return FALSE;
  1252.             FLEXTREENOTIFY &n = *((FLEXTREENOTIFY *)(LPVOID)lParam);
  1253.             if (!n.pItem)
  1254.                 return FALSE;
  1255.             switch (wParam)
  1256.             {
  1257.                 case FTN_OWNERDRAW:
  1258.                 {
  1259.                     POINT ofs = {0, 0};
  1260.                     CBitmap *pbmGlyph = NULL;
  1261.                     BOOL bAssigned = FALSE, bAssignedHere = FALSE;
  1262.                     if (n.pItem->IsUserGUID(GUID_ActionItem))
  1263.                     {
  1264.                         LPDIACTIONW lpac = GetItemLpac(n.pItem, 0);
  1265.                         if (lpac)
  1266.                             // We now walk through each DIACTION and find those with action name match, then see if 
  1267.                             // they are assigned anywhere.
  1268.                             for (DWORD i = 0; i < m_lpDiac->dwNumActions; ++i)
  1269.                             {
  1270.                                 if (wcscmp(lpac->lptszActionName, m_lpDiac->rgoAction[i].lptszActionName))
  1271.                                     continue;
  1272.  
  1273.                                 if (bAssignedHere = IsActionAssignedHere(i))
  1274.                                 {
  1275.                                     bAssigned = TRUE;
  1276.                                     break;
  1277.                                 }
  1278.                                 if (m_pUIFrame && m_pUIFrame->QueryActionAssignedAnywhere(i) == S_OK)
  1279.                                     bAssigned = TRUE;
  1280.                             }
  1281.                         if (bAssigned || bAssignedHere)
  1282.                         {
  1283.                             pbmGlyph = bAssignedHere ? m_pbmCheckGlyph :
  1284.                                 m_pbmCheckGlyphDark;
  1285.                             pbmGlyph->FigureSize();
  1286.                             ofs.x = 2;
  1287.                             ofs.y = 4;
  1288.                         }
  1289.                     }
  1290.                     else
  1291.                     {
  1292.                         if (n.pItem == m_pRelAxesParent)
  1293.                             pbmGlyph = m_pbmRelAxesGlyph;
  1294.                         if (n.pItem == m_pAbsAxesParent)
  1295.                             pbmGlyph = m_pbmAbsAxesGlyph;
  1296.                         if (n.pItem == m_pButtonParent)
  1297.                             pbmGlyph = m_pbmButtonGlyph;
  1298.                         if (n.pItem == m_pHatParent)
  1299.                             pbmGlyph = m_pbmHatGlyph;
  1300.                         ofs.y = 2;
  1301.                     }
  1302.                     if (!pbmGlyph)
  1303.                         return FALSE;
  1304.                     n.pItem->PaintInto(n.hDC);
  1305.                     RECT rect;
  1306.                     CPaintHelper ph(*m_puig, n.hDC);
  1307.                     ph.SetElement(UIE_GLYPH);
  1308.                     n.pItem->GetMargin(rect);
  1309.                     pbmGlyph->Draw(n.hDC, ofs.x, rect.top + ofs.y);
  1310.                     return TRUE;
  1311.                 }
  1312.  
  1313.                 case FTN_CLICK:
  1314.                     // We cannot assign a different control to this action if it has the DIA_APPFIXED flag.
  1315.                     if (n.pItem->IsUserGUID(GUID_ActionItem) && GetItemLpac(n.pItem) && !(GetItemLpac(n.pItem)->dwFlags & DIA_APPFIXED))
  1316.                     {
  1317.                         m_Tree.SetCurSel(n.pItem);
  1318.                         ActionClick(GetItemLpac(n.pItem));
  1319.                     }
  1320.                     else
  1321.                     {
  1322. #ifdef CFGUI__ALLOW_USER_ACTION_TREE_BRANCH_MANIPULATION
  1323.                         if (!n.pItem->IsExpanded())
  1324.                             n.pItem->Expand();
  1325.                         else
  1326.                             n.pItem->Collapse();
  1327. #endif
  1328.                     }
  1329.                     break;
  1330.             }
  1331.             break;
  1332.         }
  1333.     }
  1334.  
  1335.     return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  1336. }
  1337.  
  1338. void CDIDeviceActionConfigPage::SetInvalid(LPDIACTIONW lpac)
  1339. {
  1340.     lpac->guidInstance = GUID_NULL;
  1341.     lpac->dwObjID = (DWORD)-1;
  1342. }
  1343.  
  1344. DWORD CDIDeviceActionConfigPage::GetOffset(LPDIACTIONW lpac)
  1345. {
  1346.     return lpac ? lpac->dwObjID : (DWORD)-1;
  1347. }
  1348.  
  1349. void CDIDeviceActionConfigPage::SetOffset(LPDIACTIONW lpac, DWORD ofs)
  1350. {
  1351.     assert(lpac != NULL);
  1352.     if (!lpac)
  1353.         return;
  1354.     lpac->dwObjID = ofs;
  1355. }
  1356.  
  1357. HRESULT CDIDeviceActionConfigPage::InitLookup()
  1358. {
  1359.     DIDEVOBJSTRUCT os;
  1360.  
  1361.     HRESULT hresult = FillDIDeviceObjectStruct(os, m_lpDID);
  1362.  
  1363.     if (FAILED(hresult))
  1364.         return hresult;
  1365.  
  1366.     for (int i = 0; i < os.nObjects; i++)
  1367.     {
  1368.         DIDEVICEOBJECTINSTANCEW &doi = os.pdoi[i];
  1369.         offset_objid.add(doi.dwOfs, doi.dwType);
  1370.     }
  1371.  
  1372.     return S_OK;
  1373. }
  1374.  
  1375. HRESULT CDIDeviceActionConfigPage::SetEditLayout(BOOL bEditLayout)
  1376. {
  1377.     m_pDeviceUI->SetEditMode(bEditLayout);
  1378.     return S_OK;
  1379. }
  1380.  
  1381.  
  1382. BOOL CDIDeviceActionConfigPage::IsControlMapped(CDeviceControl *pControl)
  1383. {
  1384.     if (pControl == NULL)
  1385.         return FALSE;
  1386.  
  1387.     if (!pControl->IsOffsetAssigned())
  1388.         return FALSE;
  1389.  
  1390.     if (m_lpDiac == NULL)
  1391.         return FALSE;
  1392.     
  1393.     for (DWORD i = 0; i < m_lpDiac->dwNumActions; i++)
  1394.         if (GetOffset(&(m_lpDiac->rgoAction[i])) == pControl->GetOffset())
  1395.             return TRUE;
  1396.  
  1397.     return FALSE;
  1398. }
  1399.  
  1400. void CDIDeviceActionConfigPage::InitDevice()
  1401. {
  1402.     if (m_lpDID == NULL || m_pDeviceUI == NULL || m_pUIFrame == NULL)
  1403.         return;
  1404.  
  1405.     HWND hWndMain = m_pUIFrame->GetMainHWND();
  1406.     if (!hWndMain)
  1407.         return;
  1408.  
  1409.     // don't do anything if this is a mouse
  1410.     switch ((DWORD)(LOBYTE(LOWORD(m_pDeviceUI->m_didi.dwDevType))))
  1411.     {
  1412.         case DI8DEVTYPE_MOUSE:
  1413.             return;
  1414.     }
  1415.  
  1416.     // init/prepare...
  1417.     int i;
  1418.     const DIDEVOBJSTRUCT &os = m_pDeviceUI->m_os;
  1419.     int nObjects = os.nObjects;
  1420.  
  1421.     DIDATAFORMAT df;
  1422.     df.dwSize = sizeof(DIDATAFORMAT);
  1423.     df.dwObjSize = sizeof(DIOBJECTDATAFORMAT);
  1424.     df.dwFlags = DIDF_ABSAXIS;
  1425.     df.dwDataSize = sizeof(DWORD) * (DWORD)nObjects;
  1426.     df.dwNumObjs = (DWORD)nObjects;
  1427.     df.rgodf = (DIOBJECTDATAFORMAT *)malloc(sizeof(DIOBJECTDATAFORMAT) * nObjects);
  1428.     if (df.rgodf == NULL)
  1429.     {
  1430.         etrace1(_T("Could not allocate DIOBJECTDATAFORMAT array of %d elements\n"), nObjects);
  1431.         return;
  1432.     }
  1433.  
  1434.     m_cbDeviceDataSize = df.dwDataSize;
  1435.     for (int c = 0; c < 2; c++)
  1436.     {
  1437.         if (m_pDeviceData[c] != NULL)
  1438.             free(m_pDeviceData[c]);
  1439.         m_pDeviceData[c] = (DWORD *)malloc(m_cbDeviceDataSize);
  1440.         if (m_pDeviceData[c] == NULL)
  1441.             etrace2(_T("Could not allocate device data buffer %d of %d bytes\n"), c, m_cbDeviceDataSize);
  1442.     }
  1443.     m_nOnDeviceData = 0;
  1444.     m_bFirstDeviceData = TRUE;
  1445.  
  1446.     for (i = 0; i < nObjects; i++)
  1447.     {
  1448.         DIOBJECTDATAFORMAT *podf = &(df.rgodf[i]);
  1449.         podf->pguid = NULL;
  1450.         podf->dwOfs = i * sizeof(DWORD);
  1451.         podf->dwType = os.pdoi[i].dwType;
  1452.         podf->dwFlags = 0;
  1453.     }
  1454.  
  1455.     if (df.rgodf != NULL)
  1456.     {
  1457.         HRESULT hr = m_lpDID->SetDataFormat(&df);
  1458.         free(df.rgodf);
  1459.         df.rgodf = NULL;
  1460.  
  1461.         if (FAILED(hr))
  1462.         {
  1463.             etrace1(_T("SetDataFormat() failed, returning 0x%08x\n"), hr);
  1464.         }
  1465.         else
  1466.         {
  1467.             hr = m_lpDID->SetCooperativeLevel(hWndMain, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE);
  1468.             if (FAILED(hr))
  1469.                 etrace1(_T("SetCooperativeLevel() failed, returning 0x%08x\n"), hr);
  1470.  
  1471.             DIPROPRANGE range;
  1472.             range.diph.dwSize = sizeof(DIPROPRANGE);
  1473.             range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1474.             range.diph.dwObj = 0;
  1475.             range.diph.dwHow = DIPH_DEVICE;
  1476.             range.lMin = DEVICE_POLLING_AXIS_MIN;
  1477.             range.lMax = DEVICE_POLLING_AXIS_MAX;
  1478.  
  1479.             hr = m_lpDID->SetProperty(DIPROP_RANGE, (LPCDIPROPHEADER)&range);
  1480.             if (FAILED(hr))
  1481.                 etrace1(_T("SetProperty(DIPROP_RANGE, ...) failed, returning 0x%08x\n"), hr);
  1482.  
  1483.             hr = m_lpDID->Acquire();
  1484.             if (FAILED(hr))
  1485.                 etrace1(_T("Acquire() failed, returning 0x%08x\n"), hr);
  1486.         }
  1487.     }
  1488. }
  1489.  
  1490. void CALLBACK CDIDeviceActionConfigPage::DeviceTimerProc(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
  1491. {
  1492.     if (!IsWindow((HWND)dwUser)) return;  // Verify that dwUser is a valid window handle
  1493.     CDIDeviceActionConfigPage *pPage = (CDIDeviceActionConfigPage *)GetFlexWnd((HWND)dwUser);  // Get flex object
  1494.     if (pPage)
  1495.         pPage->DeviceTimer();
  1496. }
  1497.  
  1498. void CDIDeviceActionConfigPage::DeviceTimer()
  1499. {
  1500.     DWORD *pOldData = m_pDeviceData[m_nOnDeviceData];
  1501.     m_nOnDeviceData = (m_nOnDeviceData + 1) & 1;
  1502.     DWORD *pData = m_pDeviceData[m_nOnDeviceData];
  1503.  
  1504.     if (m_lpDID == NULL || pData == NULL || pOldData == NULL)
  1505.     {
  1506.         // Required data not available.  Return and there'll be no more timer callbacks.
  1507.         etrace(_T("DeviceTimer() failed\n"));
  1508.         return;
  1509.     }
  1510.  
  1511.     // Get device data only if this page is visible.
  1512.     if (m_lpDiac)
  1513.     {
  1514.         HRESULT hr = m_lpDID->Poll();
  1515.         if (SUCCEEDED(hr))
  1516.         {
  1517.             hr = m_lpDID->GetDeviceState(m_cbDeviceDataSize, pData);
  1518.             if (SUCCEEDED(hr))
  1519.             {
  1520.                 if (!m_bFirstDeviceData)
  1521.                 {
  1522.                     DeviceDelta(pData, pOldData);
  1523.                 } else
  1524.                 {
  1525.                     m_bFirstDeviceData = FALSE;
  1526.                 }
  1527.             } else
  1528.             {
  1529.                 etrace1(_T("GetDeviceState() failed, returning 0x%08x\n"), hr);
  1530.             }
  1531.         } else
  1532.         {
  1533.             etrace1(_T("Poll() failed, returning 0x%08x\n"), hr);
  1534.         }
  1535.     }
  1536.  
  1537.     // Set the next timer event.
  1538.     if (g_fptimeSetEvent)
  1539.         g_fptimeSetEvent(DEVICE_POLLING_INTERVAL, DEVICE_POLLING_INTERVAL,
  1540.                          CDIDeviceActionConfigPage::DeviceTimerProc, (DWORD_PTR)m_hWnd, TIME_ONESHOT);
  1541. }
  1542.  
  1543. void CDIDeviceActionConfigPage::DeviceDelta(DWORD *pData, DWORD *pOldData)
  1544. {
  1545.     if (pData == NULL || pOldData == NULL || m_pDeviceUI == NULL)
  1546.         return;
  1547.  
  1548.     const DIDEVOBJSTRUCT &os = m_pDeviceUI->m_os;
  1549.  
  1550.     // see which objects changed
  1551.     for (int i = 0; i < os.nObjects; i++)
  1552.     {
  1553.         // for axes, we need to do special processing
  1554.         if (os.pdoi[i].dwType & DIDFT_AXIS)
  1555.         {
  1556.             BOOL bSig = FALSE, bOldSig = FALSE;
  1557.  
  1558.             StoreAxisDeltaAndCalcSignificance(os.pdoi[i],
  1559.                 pData[i], pOldData[i], bSig, bOldSig);
  1560.  
  1561.             AxisDelta(os.pdoi[i], bSig, bOldSig);
  1562.  
  1563.             continue;
  1564.         }
  1565.  
  1566.         // for all others, skip that which didn't change
  1567.         if (pData[i] == pOldData[i])
  1568.             continue;
  1569.  
  1570.         // pass to appropriate delta function
  1571.         DWORD dwObjId = os.pdoi[i].dwType;
  1572.         if (dwObjId & DIDFT_BUTTON)
  1573.             ButtonDelta(os.pdoi[i], pData[i], pOldData[i]);
  1574.         else if (dwObjId & DIDFT_POV)
  1575.             PovDelta(os.pdoi[i], pData[i], pOldData[i]);
  1576.     }
  1577. }
  1578.  
  1579. void CDIDeviceActionConfigPage::StoreAxisDeltaAndCalcSignificance(const DIDEVICEOBJECTINSTANCEW &doi, DWORD data, DWORD olddata, BOOL &bSig, BOOL &bOldSig)
  1580. {
  1581.     // see if this object has an axis value array
  1582.     int i;
  1583.     if (objid_avai.getright(doi.dwType, i))
  1584.     {
  1585.         AxisValueArray &ar = m_AxisValueArray[i];
  1586.         int on = ar[0] + 1;
  1587.         if (on >= ar.GetSize())
  1588.             on = DEVICE_POLLING_ACBUF_START_INDEX;
  1589.         ar[0] = on;
  1590.         int delta = abs(int(data) - int(olddata));
  1591.         if (delta < DEVICE_POLLING_AXIS_MINDELTA)
  1592.             delta = 0;
  1593.         int cumul = ar[1];  // Retrieve cumulative value for easier processing
  1594.         cumul -= ar[on];  // Subtract value in current slot from cumul since it's being thrown away.
  1595.         cumul += delta;  // Add current delta to cumul
  1596.         ar[on] = delta;  // Store the delta at current slot
  1597.         ar[1] = cumul;  // Save cumulative value
  1598.  
  1599.         bOldSig = (BOOL)ar[2];
  1600.         ar[2] = int(bSig = cumul > DEVICE_POLLING_AXIS_SIGNIFICANT);
  1601.         if (bSig)
  1602.         {
  1603.             // This axis is about to be activated.  We now reset the history and cumulative movement since we don't need them any more.
  1604.             ar[0] = DEVICE_POLLING_ACBUF_START_INDEX;
  1605.             ar[1] = 0;
  1606.             ar[2] = FALSE;
  1607.             for (int c = DEVICE_POLLING_ACBUF_START_INDEX;
  1608.                     c < DEVICE_POLLING_ACBUF_START_INDEX + DEVICE_POLLING_AXIS_ACCUMULATION; c++)
  1609.                 ar[c] = 0;
  1610.         }
  1611.     }
  1612.     else
  1613.     {
  1614.         i = m_AxisValueArray.GetSize();
  1615.         m_AxisValueArray.SetSize(i + 1);
  1616.         objid_avai.add(doi.dwType, i);
  1617.         AxisValueArray &ar = m_AxisValueArray[i];
  1618.         ar.SetSize(DEVICE_POLLING_ACBUF_START_INDEX + DEVICE_POLLING_AXIS_ACCUMULATION);
  1619.         ar[0] = DEVICE_POLLING_ACBUF_START_INDEX;
  1620.         ar[1] = 0;
  1621.         ar[2] = FALSE;
  1622.         for (int c = DEVICE_POLLING_ACBUF_START_INDEX;
  1623.                 c < DEVICE_POLLING_ACBUF_START_INDEX + DEVICE_POLLING_AXIS_ACCUMULATION; c++)
  1624.             ar[c] = 0;
  1625.         
  1626.         bOldSig = bSig = FALSE;
  1627.     }
  1628. }
  1629.  
  1630. void CDIDeviceActionConfigPage::AxisDelta(const DIDEVICEOBJECTINSTANCEW &doi, BOOL data, BOOL old)
  1631. {
  1632.     if (data && !old)
  1633.     {
  1634.         if (m_State == CFGSTATE_NORMAL)
  1635.             ActivateObject(doi);
  1636.     }
  1637.     if (old && !data)
  1638.         DeactivateObject(doi);
  1639. }
  1640.  
  1641. void CDIDeviceActionConfigPage::ButtonDelta(const DIDEVICEOBJECTINSTANCEW &doi, DWORD data, DWORD old)
  1642. {
  1643.     static DWORD dwLastOfs;
  1644.     static DWORD dwLastTimeStamp;
  1645.  
  1646.     if (data && !old)
  1647.     {
  1648.         // Do special processing for keyboard
  1649.         if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_KEYBOARD)
  1650.         {
  1651.             // If this is an ENTER key, we enter the assign state if not already in it.
  1652.             if (doi.dwOfs == DIK_RETURN || doi.dwOfs == DIK_NUMPADENTER)
  1653.             {
  1654.                 if (m_State == CFGSTATE_NORMAL && m_pCurControl)
  1655.                     EnterAssignState();
  1656.                 return;  // Do nothing other than entering the assign state.  No highlighting
  1657.             }
  1658.  
  1659.             // DELETE key case
  1660.             // If we are in assign state and there is a control, unassign it.
  1661.             if (doi.dwOfs == DIK_DELETE && m_State == CFGSTATE_ASSIGN && m_pCurControl)
  1662.                 {
  1663.                     UnassignCallout();
  1664.                     return;  // Don't highlight or do pick to click for delete if this press happens during assign state.
  1665.                 }
  1666.  
  1667.             // ESCAPE key case
  1668.             if (doi.dwOfs == DIK_ESCAPE && m_State == CFGSTATE_ASSIGN)
  1669.             {
  1670.                 ExitAssignState();
  1671.                 return;
  1672.             }
  1673.  
  1674.             // For all other keys, still process click-to-pick or highlighting.
  1675.         }
  1676.  
  1677.         // Enter assign state if this is a double activation
  1678.         if (m_State == CFGSTATE_NORMAL)
  1679.         {
  1680.             ActivateObject(doi);
  1681.  
  1682.             if (doi.dwOfs == dwLastOfs && dwLastTimeStamp + GetDoubleClickTime() > GetTickCount())
  1683.             {
  1684.                 // We check if a callout for this control exists.  If not, do not enter assign state.
  1685.                 CDeviceView *pCurView = m_pDeviceUI->GetCurView();
  1686.                 CDeviceControl *pControl = pCurView->GetControlFromOfs(doi.dwType);
  1687.                 if (pControl)
  1688.                     EnterAssignState();
  1689.             }
  1690.             dwLastOfs = doi.dwOfs;
  1691.             dwLastTimeStamp = GetTickCount();
  1692.         }
  1693.     }
  1694.     if (old && !data)
  1695.         DeactivateObject(doi);
  1696. }
  1697.  
  1698. void CDIDeviceActionConfigPage::PovDelta(const DIDEVICEOBJECTINSTANCEW &doi, DWORD data, DWORD old)
  1699. {
  1700.     BOOL d = data != -1, o = old != -1;
  1701.  
  1702.     if (d && !o)
  1703.     {
  1704.         if (m_State == CFGSTATE_NORMAL)
  1705.             ActivateObject(doi);
  1706.     }
  1707.     if (o && !d)
  1708.         DeactivateObject(doi);    
  1709. }
  1710.  
  1711. void CDIDeviceActionConfigPage::ActivateObject(const DIDEVICEOBJECTINSTANCEW &doi)
  1712. {
  1713.     if (m_pDeviceUI == NULL)
  1714.         return;
  1715.  
  1716.  
  1717.     CDeviceView *pCurView = m_pDeviceUI->GetCurView(), *pView = pCurView;
  1718.     if (pView == NULL)
  1719.         return;
  1720.  
  1721.     CDeviceControl *pControl = pView->GetControlFromOfs(doi.dwType);
  1722.     if (pControl == NULL)
  1723.     {
  1724.         for (int i = 0; i < m_pDeviceUI->GetNumViews(); i++)
  1725.         {
  1726.             pView = m_pDeviceUI->GetView(i);
  1727.             if (pView == NULL)
  1728.                 continue;
  1729.  
  1730.             pControl = pView->GetControlFromOfs(doi.dwType);
  1731.             if (pControl != NULL)
  1732.                 break;
  1733.         }
  1734.  
  1735.         if (pControl != NULL && pView != NULL && pView != pCurView)
  1736.         {
  1737.             // switch to view
  1738.             m_pDeviceUI->SetView(pView);
  1739.             SetControlAssignments();
  1740.             SetCurrentControl(NULL);
  1741.         }
  1742.     }
  1743.  
  1744.     if (pControl != NULL)
  1745.         SetCurrentControl(pControl);
  1746.  
  1747.     SetAppropriateDefaultText();
  1748. }
  1749.  
  1750. void CDIDeviceActionConfigPage::DeactivateObject(const DIDEVICEOBJECTINSTANCEW &doi)
  1751. {
  1752.     // Add code that needs to be run when deactivating here.
  1753. }
  1754.  
  1755. HRESULT CDIDeviceActionConfigPage::Unacquire()
  1756. {
  1757.     if (m_lpDID != NULL)
  1758.         m_lpDID->Unacquire();
  1759.  
  1760.     return S_OK;
  1761. }
  1762.  
  1763. HRESULT CDIDeviceActionConfigPage::Reacquire()
  1764. {
  1765.     InitDevice();
  1766.  
  1767.     return S_OK;
  1768. }
  1769.  
  1770. void CDIDeviceActionConfigPage::EnterAssignState()
  1771. {
  1772.     if (!m_puig->InEditMode())
  1773.         return;
  1774.     if (!m_pCurControl || m_pCurControl->IsFixed())
  1775.         return;
  1776.     SetInfoText(IDS_INFOMSG_EDIT_EDITMODEENABLED);
  1777.     m_State = CFGSTATE_ASSIGN;  // Into the assign state.
  1778.     ShowCurrentControlAssignment();  // Show the tree
  1779.     m_Tree.Invalidate();
  1780.     Invalidate();
  1781. }
  1782.  
  1783. void CDIDeviceActionConfigPage::ExitAssignState()
  1784. {
  1785.     m_State = CFGSTATE_NORMAL;  // Out of the assign state.
  1786.     SetCurrentControl(NULL);  // Unselect the control
  1787.     ShowCurrentControlAssignment();  // Show the tree
  1788.     m_Tree.Invalidate();
  1789.     Invalidate();
  1790.     SetAppropriateDefaultText();
  1791. }
  1792.  
  1793. HRESULT CDIDeviceActionConfigPage::SetInfoText(int iCode)
  1794. {
  1795.     // We check for special code -1 here.  This is only called by CConfigWnd, and means that we should
  1796.     // call SetAppropriateDefaultText to display proper text.
  1797.     if (iCode == -1)
  1798.         SetAppropriateDefaultText();
  1799.     else
  1800.         m_InfoBox.SetText(iCode);
  1801.     return S_OK;
  1802. }
  1803.  
  1804. void CDIDeviceActionConfigPage::SetAppropriateDefaultText()
  1805. {
  1806.     if (m_puig->InEditMode())
  1807.     {
  1808.         if (m_State == CFGSTATE_ASSIGN)
  1809.             SetInfoText(IDS_INFOMSG_EDIT_EDITMODEENABLED);
  1810.         else if (m_pCurControl)
  1811.         {
  1812.             if (m_pCurControl->IsFixed())
  1813.                 SetInfoText(IDS_INFOMSG_APPFIXEDSELECT);
  1814.             else
  1815.                 SetInfoText(IDS_INFOMSG_EDIT_CTRLSELECTED);
  1816.         }
  1817.         else
  1818.         {
  1819.             if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_KEYBOARD)
  1820.                 SetInfoText(IDS_INFOMSG_EDIT_KEYBOARD);
  1821.             else
  1822.             if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_MOUSE)
  1823.                 SetInfoText(IDS_INFOMSG_EDIT_MOUSE);
  1824.             else
  1825.                 SetInfoText(IDS_INFOMSG_EDIT_DEVICE);
  1826.         }
  1827.     } else
  1828.         SetInfoText(IDS_INFOMSG_VIEW_DEVICE);
  1829. }
  1830.