Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

562 lines
15 KiB

  1. //-----------------------------------------------------------------------------
  2. // File: populate.cpp
  3. //
  4. // Desc: This file contains the population functions. These are all
  5. // accessed through PopulateAppropriately(). That function creates
  6. // views & controls based on the type of the device that the passed
  7. // DeviceUI represents.
  8. //
  9. // Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
  10. //-----------------------------------------------------------------------------
  11. #include "common.hpp"
  12. // these functions are internal to this filed, called only by
  13. // PopulateAppropriately().
  14. HRESULT PopulateViaGetImageInfo(CDeviceUI &ui);
  15. HRESULT PopulateFromImageInfoHeader(CDeviceUI &ui, const DIDEVICEIMAGEINFOHEADERW &);
  16. HRESULT PopulateListView(CDeviceUI &ui);
  17. HRESULT PopulateErrorView(CDeviceUI &ui);
  18. // Clears the entire passed DeviceUI, then fills it with views and
  19. // controls based on device type. Tries to gaurantee that there will
  20. // be at least one view.
  21. HRESULT PopulateAppropriately(CDeviceUI &ui)
  22. {
  23. HRESULT hr = S_OK;
  24. // first empty the ui
  25. ui.Unpopulate();
  26. // get device type
  27. DWORD dwdt = ui.m_didi.dwDevType;
  28. DWORD dwType = (DWORD)(LOBYTE(LOWORD(dwdt)));
  29. DWORD dwSubType = (DWORD)(HIBYTE(LOWORD(dwdt)));
  30. // based on type...
  31. switch (dwType)
  32. {
  33. default:
  34. // unless its a type we don't ever want views for,
  35. // populate via the GetImageInfo() API
  36. hr = PopulateViaGetImageInfo(ui);
  37. if (SUCCEEDED(hr) && ui.GetNumViews() > 0)
  38. return hr;
  39. // if it failed or resulted in nothing,
  40. // clear anything that might've been added
  41. ui.Unpopulate();
  42. // intentional fallthrough
  43. case DI8DEVTYPE_MOUSE:
  44. case DI8DEVTYPE_KEYBOARD:
  45. //@@BEGIN_MSINTERNAL
  46. #ifdef DDKBUILD
  47. // don't do list view if we're in edit layout mode
  48. if (ui.m_uig.QueryAllowEditLayout())
  49. goto doerrorview;
  50. #endif
  51. //@@END_MSINTERNAL
  52. // for types that we don't ever want views for
  53. // we populate the list view without trying the above
  54. hr = PopulateListView(ui);
  55. // if we still failed or don't have any views,
  56. // populate with error message view
  57. if (FAILED(hr) || ui.GetNumViews() < 1)
  58. {
  59. // empty
  60. ui.Unpopulate();
  61. // show error message
  62. //@@BEGIN_MSINTERNAL
  63. #ifdef DDKBUILD
  64. doerrorview:
  65. #endif
  66. //@@END_MSINTERNAL
  67. hr = PopulateErrorView(ui);
  68. }
  69. // this function should guarantee success
  70. assert(!FAILED(hr));
  71. return hr;
  72. }
  73. }
  74. // Calls the GetImageInfo() API to get the view images and controls
  75. // for the entire device, and returns a failure if there's the
  76. // slightest problem (if GII() fails, or if an image fails to load,
  77. // etc.)
  78. HRESULT PopulateViaGetImageInfo(CDeviceUI &ui)
  79. {
  80. if (!ui.m_lpDID)
  81. return E_FAIL;
  82. HRESULT hr = S_OK;
  83. DIDEVICEIMAGEINFOHEADERW m_diImgInfoHdr;
  84. LPDIDEVICEIMAGEINFOW &lprgdiImgData = m_diImgInfoHdr.lprgImageInfoArray;
  85. ZeroMemory( &m_diImgInfoHdr, sizeof(DIDEVICEIMAGEINFOHEADERW) );
  86. m_diImgInfoHdr.dwSize = sizeof(DIDEVICEIMAGEINFOHEADERW);
  87. m_diImgInfoHdr.dwSizeImageInfo = sizeof(DIDEVICEIMAGEINFOW);
  88. // Retrieve the required buffer size.
  89. hr = ui.m_lpDID->GetImageInfo( &m_diImgInfoHdr );
  90. if (FAILED(hr))
  91. {
  92. etrace1(_T("GetImageInfo() failed while trying to get required buffer size. hr = 0x%08x\n"), hr);
  93. return E_FAIL;
  94. }
  95. // Allocate the buffer.
  96. lprgdiImgData = (LPDIDEVICEIMAGEINFOW) malloc( (size_t)
  97. (m_diImgInfoHdr.dwBufferSize = m_diImgInfoHdr.dwBufferUsed) );
  98. if (lprgdiImgData == NULL)
  99. {
  100. etrace1(_T("Could not allocate buffer of size %d.\n"), m_diImgInfoHdr.dwBufferSize);
  101. return E_FAIL;
  102. }
  103. trace(_T("Allocated buffer.\n"));
  104. traceDWORD(m_diImgInfoHdr.dwBufferSize);
  105. m_diImgInfoHdr.lprgImageInfoArray = lprgdiImgData;
  106. // Get the display info.
  107. hr = ui.m_lpDID->GetImageInfo( &m_diImgInfoHdr );
  108. if (FAILED(hr))
  109. {
  110. etrace1(_T("GetImageInfo() failed trying to get image info. hr = 0x%08x\n"), hr);
  111. free(lprgdiImgData);
  112. lprgdiImgData = NULL;
  113. return E_FAIL;
  114. }
  115. // actually populate now
  116. traceDWORD(m_diImgInfoHdr.dwBufferUsed);
  117. hr = PopulateFromImageInfoHeader(ui, m_diImgInfoHdr);
  118. if (FAILED(hr))
  119. return hr;
  120. // free stuff
  121. free(lprgdiImgData);
  122. lprgdiImgData = NULL;
  123. return S_OK;
  124. }
  125. // basically does the work for the above function after the header
  126. // is actually retrieved
  127. HRESULT PopulateFromImageInfoHeader(CDeviceUI &ui, const DIDEVICEIMAGEINFOHEADERW &dih)
  128. {
  129. tracescope(ts1, _T("CGetImageInfoPopHelper::Init()...\n"));
  130. traceDWORD(dih.dwSizeImageInfo);
  131. traceDWORD(dih.dwBufferSize);
  132. traceDWORD(dih.dwBufferUsed);
  133. if (dih.dwSizeImageInfo != sizeof(DIDEVICEIMAGEINFOW))
  134. {
  135. etrace(_T("dwSizeImageInfo Incorrect.\n"));
  136. assert(0);
  137. return E_FAIL;
  138. }
  139. DWORD dwNumElements = dih.dwBufferUsed / dih.dwSizeImageInfo;
  140. if (dwNumElements * dih.dwSizeImageInfo != dih.dwBufferUsed
  141. || dih.dwBufferUsed < dih.dwBufferSize)
  142. {
  143. etrace(_T("Could not confidently calculate dwNumElements.\n"));
  144. assert(0);
  145. return E_FAIL;
  146. }
  147. DWORD i;
  148. traceDWORD(dwNumElements);
  149. bidirlookup<DWORD, int> offset_view;
  150. {
  151. tracescope(ts2, _T("First Pass...\n"));
  152. for (i = 0; i < dwNumElements; i++)
  153. if (dih.lprgImageInfoArray[i].dwFlags & DIDIFT_CONFIGURATION)
  154. {
  155. LPDIDEVICEIMAGEINFOW lpInfoBase = dih.lprgImageInfoArray;
  156. DWORD index = i;
  157. {
  158. tracescope(ts1, _T("AddViewInfo()...\n"));
  159. traceHEXPTR(lpInfoBase);
  160. traceDWORD(index);
  161. if (lpInfoBase == NULL)
  162. {
  163. etrace(_T("lpInfoBase NULL\n"));
  164. return E_FAIL;
  165. }
  166. DIDEVICEIMAGEINFOW &info = lpInfoBase[index];
  167. DWORD dwOffset = index;
  168. // add view info to array
  169. CDeviceView *pView = ui.NewView();
  170. if (!pView)
  171. {
  172. etrace(_T("Could not create new view.\n"));
  173. return E_FAIL;
  174. }
  175. int nView = pView->GetViewIndex();
  176. tracescope(ts2, _T("Adding View "));
  177. trace2(_T("%d (info index %u)\n"), nView, index);
  178. // set view's imagepath
  179. if (!info.tszImagePath)
  180. {
  181. etrace(_T("No image path.\n"));
  182. return E_FAIL;
  183. }
  184. LPTSTR tszImagePath = AllocLPTSTR(info.tszImagePath);
  185. if (!tszImagePath)
  186. {
  187. etrace(_T("Could not copy image path.\n"));
  188. return E_FAIL;
  189. }
  190. // set the view's image path
  191. pView->SetImagePath(tszImagePath);
  192. // create bitmap from path
  193. LPDIRECT3DSURFACE8 lpSurf3D = ui.m_uig.GetSurface3D();
  194. CBitmap *pbm = CBitmap::CreateViaD3DX(tszImagePath, lpSurf3D);
  195. traceSTR(info.tszImagePath);
  196. traceHEXPTR(pbm);
  197. traceDWORD(dwOffset);
  198. free(tszImagePath);
  199. tszImagePath = NULL;
  200. if (lpSurf3D)
  201. {
  202. lpSurf3D->Release(); // Need to free the surface instance after we are done as AddRef() was called earlier.
  203. lpSurf3D = NULL;
  204. }
  205. if (!pbm)
  206. {
  207. etrace(_T("Could not create image from path.\n"));
  208. return E_FAIL;
  209. }
  210. // set the view's image
  211. assert(pbm != NULL);
  212. pView->SetImage(pbm); // setimage steals the bitmap pointer
  213. assert(pbm == NULL);
  214. // add conversion from offset to view
  215. offset_view.add(dwOffset, nView);
  216. }
  217. }
  218. }
  219. {
  220. tracescope(ts2, _T("Second Pass...\n"));
  221. for (i = 0; i < dwNumElements; i++)
  222. {
  223. DWORD dwFlags = dih.lprgImageInfoArray[i].dwFlags;
  224. if (dwFlags & DIDIFT_OVERLAY)
  225. {
  226. LPDIDEVICEIMAGEINFOW lpInfoBase = dih.lprgImageInfoArray;
  227. DWORD index = i;
  228. {
  229. tracescope(ts1, _T("AddControlInfo()...\n"));
  230. traceHEXPTR(lpInfoBase);
  231. traceDWORD(index);
  232. if (lpInfoBase == NULL)
  233. {
  234. etrace(_T("lpInfoBase NULL\n"));
  235. return E_FAIL;
  236. }
  237. DIDEVICEIMAGEINFOW &info = lpInfoBase[index];
  238. int nViewIndex = 0;
  239. if (!offset_view.getright(info.dwViewID, nViewIndex))
  240. {
  241. etrace(_T("Could not get view index\n"));
  242. return E_FAIL;
  243. }
  244. if (nViewIndex < 0 || nViewIndex >= ui.GetNumViews())
  245. {
  246. etrace1(_T("Invalid view index %d\n"), nViewIndex);
  247. return E_FAIL;
  248. }
  249. CDeviceView *pView = ui.GetView(nViewIndex);
  250. if (!pView)
  251. {
  252. etrace1(_T("\n"), nViewIndex);
  253. return E_FAIL;
  254. }
  255. CDeviceControl *pControl = pView->NewControl();
  256. int nControl = pControl->GetControlIndex();
  257. tracescope(ts2, _T("Adding Control "));
  258. trace4(_T("%d (info index %u) to view %d (info index %u)\n"), nControl, index, nViewIndex, info.dwViewID);
  259. traceDWORD(info.dwObjID);
  260. traceDWORD(info.dwcValidPts);
  261. traceRECT(info.rcCalloutRect);
  262. traceRECT(info.rcOverlay);
  263. traceHEX(info.dwTextAlign);
  264. traceSTR(info.tszImagePath);
  265. pControl->SetObjID(info.dwObjID);
  266. pControl->SetLinePoints(int(info.dwcValidPts), info.rgptCalloutLine);
  267. pControl->SetCalloutMaxRect(info.rcCalloutRect);
  268. pControl->SetAlignment(info.dwTextAlign);
  269. if (info.tszImagePath)
  270. {
  271. LPTSTR tszOverlayPath = AllocLPTSTR(info.tszImagePath);
  272. if (tszOverlayPath)
  273. pControl->SetOverlayPath(tszOverlayPath);
  274. free(tszOverlayPath);
  275. tszOverlayPath = NULL;
  276. }
  277. pControl->SetOverlayRect(info.rcOverlay);
  278. pControl->Init();
  279. }
  280. }
  281. }
  282. }
  283. return S_OK;
  284. }
  285. // Enumerates the controls on the device and creates one big list
  286. // view for the device. Fails if it can't enumerate for some reason.
  287. HRESULT PopulateListView(CDeviceUI &ui)
  288. {
  289. int i;
  290. HRESULT hr = S_OK;
  291. // we must have the device interface
  292. if (!ui.m_lpDID)
  293. return E_FAIL;
  294. // create one view
  295. CDeviceView *pView = ui.NewView();
  296. if (!pView)
  297. return E_FAIL;
  298. // enable scrolling on it
  299. pView->EnableScrolling();
  300. // get list of controls
  301. DIDEVOBJSTRUCT os;
  302. hr = FillDIDeviceObjectStruct(os, ui.m_lpDID);
  303. if (FAILED(hr))
  304. return hr;
  305. // if there aren't any, fail
  306. int n = os.nObjects;
  307. if (n < 1)
  308. return E_FAIL;
  309. HDC hDC = CreateCompatibleDC(NULL);
  310. CPaintHelper ph(ui.m_uig, hDC);
  311. ph.SetElement(UIE_DEVOBJ);
  312. // Initially, max width is the width needed for the Control label.
  313. TCHAR tszHeader[MAX_PATH];
  314. RECT LabelRect = {0, 0, 0, 0};
  315. LoadString(g_hModule, IDS_LISTHEADER_CTRL, tszHeader, MAX_PATH);
  316. DrawText(hDC, tszHeader, -1, &LabelRect, DT_LEFT|DT_NOPREFIX|DT_CALCRECT);
  317. // run through and create a text for every control to
  318. // get the sizing
  319. POINT origin = {0, 0};
  320. SIZE max = {LabelRect.right - LabelRect.left, 0};
  321. for (i = 0; i < n; i++)
  322. {
  323. LPTSTR tszName = AllocLPTSTR(os.pdoi[i].tszName);
  324. CDeviceViewText *pText = pView->AddText(
  325. (HFONT)ui.m_uig.GetFont(UIE_DEVOBJ),
  326. ui.m_uig.GetTextColor(UIE_DEVOBJ),
  327. ui.m_uig.GetBkColor(UIE_DEVOBJ),
  328. origin,
  329. tszName);
  330. free(tszName);
  331. if (!pText)
  332. {
  333. DeleteDC(hDC);
  334. return E_FAIL;
  335. }
  336. SIZE tsize = GetRectSize(pText->GetRect());
  337. if (tsize.cx > max.cx)
  338. max.cx = tsize.cx;
  339. if (tsize.cy > max.cy)
  340. max.cy = tsize.cy;
  341. }
  342. // Find out if we should use one column or two columns if this is a kbd device.
  343. BOOL bUseTwoColumns = FALSE;
  344. if (LOBYTE(LOWORD(ui.m_didi.dwDevType)) == DI8DEVTYPE_KEYBOARD &&
  345. ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1) - max.cx >= MINLISTVIEWCALLOUTWIDTH)
  346. bUseTwoColumns = TRUE;
  347. // Do two iterations here. First one we use two columns for keyboard. 2nd one is
  348. // run only if the header labels are clipped. In which case a single column is used.
  349. for (int iPass = 0; iPass < 2; ++iPass)
  350. {
  351. // calculate max callout height based on the two possible fonts
  352. int cmaxh = 0,
  353. ch = 2 + GetTextHeight((HFONT)ui.m_uig.GetFont(UIE_CALLOUT)),
  354. chh = 2 + GetTextHeight((HFONT)ui.m_uig.GetFont(UIE_CALLOUTHIGH));
  355. if (ch > cmaxh)
  356. cmaxh = ch;
  357. if (chh > cmaxh)
  358. cmaxh = chh;
  359. // calculate the bigger of text/callout
  360. int h = 0;
  361. if (cmaxh > h)
  362. h = cmaxh;
  363. if (max.cy > h)
  364. h = max.cy;
  365. // calculate vertical offsets of text/callout within max spacing
  366. int to = (h - max.cy) / 2,
  367. co = (h - cmaxh) / 2;
  368. // max width for text is half of the view window
  369. if (max.cx > ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1))
  370. max.cx = ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1);
  371. // go back through all the controls and place the text while
  372. // creating the corresponding callouts
  373. int at = 0; // Start at second row since first row is used for header. Also half row spacing
  374. for (i = 0; i < n; i++)
  375. {
  376. // reposition the text
  377. CDeviceViewText *pText = pView->GetText(i);
  378. if (!pText)
  379. {
  380. DeleteDC(hDC);
  381. return E_FAIL;
  382. }
  383. SIZE s = GetRectSize(pText->GetRect());
  384. if (bUseTwoColumns)
  385. {
  386. int iXOffset = i & 1 ? ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1) : 0;
  387. RECT rect = {iXOffset,
  388. at + to,
  389. max.cx + iXOffset,
  390. at + to + s.cy};
  391. // Get the rectangle that is actually used.
  392. RECT adjrect = rect;
  393. if (hDC)
  394. {
  395. DrawText(hDC, pText->GetText(), -1, &adjrect, DT_NOPREFIX|DT_CALCRECT);
  396. // If the rect actually used is smaller than the space available, use the smaller rect and align to right.
  397. if (adjrect.right < rect.right)
  398. rect.left += rect.right - adjrect.right;
  399. }
  400. pText->SetRect(rect);
  401. }
  402. else
  403. {
  404. RECT rect = {0, at + to, max.cx /*> ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1) ?
  405. ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1) : max.cx*/, at + to + s.cy};
  406. pText->SetRect(rect);
  407. }
  408. // create the control
  409. CDeviceControl *pControl = pView->NewControl();
  410. if (!pControl)
  411. {
  412. DeleteDC(hDC);
  413. return E_FAIL;
  414. }
  415. // position it
  416. RECT rect = {max.cx + 10, at, (g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1, at + h};
  417. // If single column, extend callout all the way to right end of view window
  418. if (!bUseTwoColumns)
  419. rect.right = g_sizeImage.cx - DEFAULTVIEWSBWIDTH;
  420. // If this is a keyboard, move to right column on odd numbered controls.
  421. if (bUseTwoColumns && (i & 1))
  422. {
  423. rect.left += (g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1;
  424. rect.right = g_sizeImage.cx - DEFAULTVIEWSBWIDTH;
  425. }
  426. pControl->SetCalloutMaxRect(rect);
  427. // align it
  428. pControl->SetAlignment(CAF_LEFT);
  429. // set approp offset
  430. pControl->SetObjID(os.pdoi[i].dwType);
  431. // init it
  432. pControl->Init();
  433. // go to next y coord
  434. // If this is a keyboard, then only increase y when we are moving to even numbered controls.
  435. if (!bUseTwoColumns || (i & 1))
  436. at += h;
  437. }
  438. // Compute the rectangles for header labels
  439. if (pView->CalculateHeaderRect() && iPass == 0)
  440. {
  441. pView->RemoveAll();
  442. bUseTwoColumns = FALSE; // Re-calculate the rects using single column.
  443. }
  444. else
  445. break; // Break out from 2nd iteration
  446. }
  447. DeleteDC(hDC);
  448. // make selection/thumb images (just for kicks)
  449. pView->MakeMissingImages();
  450. // calculate view dimensions (for scrolling)
  451. pView->CalcDimensions();
  452. return S_OK;
  453. }
  454. // Creates a single view with an error message. Should not fail.
  455. HRESULT PopulateErrorView(CDeviceUI &ui)
  456. {
  457. // create the new view
  458. CDeviceView *pView = ui.NewView();
  459. if (!pView)
  460. return E_FAIL;
  461. // add text objects containing error message
  462. pView->AddWrappedLineOfText(
  463. (HFONT)ui.m_uig.GetFont(UIE_ERRORHEADER),
  464. ui.m_uig.GetTextColor(UIE_ERRORHEADER),
  465. ui.m_uig.GetBkColor(UIE_ERRORHEADER),
  466. _T("Error!"));
  467. pView->AddWrappedLineOfText(
  468. (HFONT)ui.m_uig.GetFont(UIE_ERRORMESSAGE),
  469. ui.m_uig.GetTextColor(UIE_ERRORMESSAGE),
  470. ui.m_uig.GetBkColor(UIE_ERRORMESSAGE),
  471. _T("Could not create views for device."));
  472. pView->MakeMissingImages();
  473. return S_OK;
  474. }