//----------------------------------------------------------------------------- // File: populate.cpp // // Desc: This file contains the population functions. These are all // accessed through PopulateAppropriately(). That function creates // views & controls based on the type of the device that the passed // DeviceUI represents. // // Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved. //----------------------------------------------------------------------------- #include "common.hpp" // these functions are internal to this filed, called only by // PopulateAppropriately(). HRESULT PopulateViaGetImageInfo(CDeviceUI &ui); HRESULT PopulateFromImageInfoHeader(CDeviceUI &ui, const DIDEVICEIMAGEINFOHEADERW &); HRESULT PopulateListView(CDeviceUI &ui); HRESULT PopulateErrorView(CDeviceUI &ui); // Clears the entire passed DeviceUI, then fills it with views and // controls based on device type. Tries to gaurantee that there will // be at least one view. HRESULT PopulateAppropriately(CDeviceUI &ui) { HRESULT hr = S_OK; // first empty the ui ui.Unpopulate(); // get device type DWORD dwdt = ui.m_didi.dwDevType; DWORD dwType = (DWORD)(LOBYTE(LOWORD(dwdt))); DWORD dwSubType = (DWORD)(HIBYTE(LOWORD(dwdt))); // based on type... switch (dwType) { default: // unless its a type we don't ever want views for, // populate via the GetImageInfo() API hr = PopulateViaGetImageInfo(ui); if (SUCCEEDED(hr) && ui.GetNumViews() > 0) return hr; // if it failed or resulted in nothing, // clear anything that might've been added ui.Unpopulate(); // intentional fallthrough case DI8DEVTYPE_MOUSE: case DI8DEVTYPE_KEYBOARD: //@@BEGIN_MSINTERNAL #ifdef DDKBUILD // don't do list view if we're in edit layout mode if (ui.m_uig.QueryAllowEditLayout()) goto doerrorview; #endif //@@END_MSINTERNAL // for types that we don't ever want views for // we populate the list view without trying the above hr = PopulateListView(ui); // if we still failed or don't have any views, // populate with error message view if (FAILED(hr) || ui.GetNumViews() < 1) { // empty ui.Unpopulate(); // show error message //@@BEGIN_MSINTERNAL #ifdef DDKBUILD doerrorview: #endif //@@END_MSINTERNAL hr = PopulateErrorView(ui); } // this function should guarantee success assert(!FAILED(hr)); return hr; } } // Calls the GetImageInfo() API to get the view images and controls // for the entire device, and returns a failure if there's the // slightest problem (if GII() fails, or if an image fails to load, // etc.) HRESULT PopulateViaGetImageInfo(CDeviceUI &ui) { if (!ui.m_lpDID) return E_FAIL; HRESULT hr = S_OK; DIDEVICEIMAGEINFOHEADERW m_diImgInfoHdr; LPDIDEVICEIMAGEINFOW &lprgdiImgData = m_diImgInfoHdr.lprgImageInfoArray; ZeroMemory( &m_diImgInfoHdr, sizeof(DIDEVICEIMAGEINFOHEADERW) ); m_diImgInfoHdr.dwSize = sizeof(DIDEVICEIMAGEINFOHEADERW); m_diImgInfoHdr.dwSizeImageInfo = sizeof(DIDEVICEIMAGEINFOW); // Retrieve the required buffer size. hr = ui.m_lpDID->GetImageInfo( &m_diImgInfoHdr ); if (FAILED(hr)) { etrace1(_T("GetImageInfo() failed while trying to get required buffer size. hr = 0x%08x\n"), hr); return E_FAIL; } // Allocate the buffer. lprgdiImgData = (LPDIDEVICEIMAGEINFOW) malloc( (size_t) (m_diImgInfoHdr.dwBufferSize = m_diImgInfoHdr.dwBufferUsed) ); if (lprgdiImgData == NULL) { etrace1(_T("Could not allocate buffer of size %d.\n"), m_diImgInfoHdr.dwBufferSize); return E_FAIL; } trace(_T("Allocated buffer.\n")); traceDWORD(m_diImgInfoHdr.dwBufferSize); m_diImgInfoHdr.lprgImageInfoArray = lprgdiImgData; // Get the display info. hr = ui.m_lpDID->GetImageInfo( &m_diImgInfoHdr ); if (FAILED(hr)) { etrace1(_T("GetImageInfo() failed trying to get image info. hr = 0x%08x\n"), hr); free(lprgdiImgData); lprgdiImgData = NULL; return E_FAIL; } // actually populate now traceDWORD(m_diImgInfoHdr.dwBufferUsed); hr = PopulateFromImageInfoHeader(ui, m_diImgInfoHdr); if (FAILED(hr)) return hr; // free stuff free(lprgdiImgData); lprgdiImgData = NULL; return S_OK; } // basically does the work for the above function after the header // is actually retrieved HRESULT PopulateFromImageInfoHeader(CDeviceUI &ui, const DIDEVICEIMAGEINFOHEADERW &dih) { tracescope(ts1, _T("CGetImageInfoPopHelper::Init()...\n")); traceDWORD(dih.dwSizeImageInfo); traceDWORD(dih.dwBufferSize); traceDWORD(dih.dwBufferUsed); if (dih.dwSizeImageInfo != sizeof(DIDEVICEIMAGEINFOW)) { etrace(_T("dwSizeImageInfo Incorrect.\n")); assert(0); return E_FAIL; } DWORD dwNumElements = dih.dwBufferUsed / dih.dwSizeImageInfo; if (dwNumElements * dih.dwSizeImageInfo != dih.dwBufferUsed || dih.dwBufferUsed < dih.dwBufferSize) { etrace(_T("Could not confidently calculate dwNumElements.\n")); assert(0); return E_FAIL; } DWORD i; traceDWORD(dwNumElements); bidirlookup offset_view; { tracescope(ts2, _T("First Pass...\n")); for (i = 0; i < dwNumElements; i++) if (dih.lprgImageInfoArray[i].dwFlags & DIDIFT_CONFIGURATION) { LPDIDEVICEIMAGEINFOW lpInfoBase = dih.lprgImageInfoArray; DWORD index = i; { tracescope(ts1, _T("AddViewInfo()...\n")); traceHEXPTR(lpInfoBase); traceDWORD(index); if (lpInfoBase == NULL) { etrace(_T("lpInfoBase NULL\n")); return E_FAIL; } DIDEVICEIMAGEINFOW &info = lpInfoBase[index]; DWORD dwOffset = index; // add view info to array CDeviceView *pView = ui.NewView(); if (!pView) { etrace(_T("Could not create new view.\n")); return E_FAIL; } int nView = pView->GetViewIndex(); tracescope(ts2, _T("Adding View ")); trace2(_T("%d (info index %u)\n"), nView, index); // set view's imagepath if (!info.tszImagePath) { etrace(_T("No image path.\n")); return E_FAIL; } LPTSTR tszImagePath = AllocLPTSTR(info.tszImagePath); if (!tszImagePath) { etrace(_T("Could not copy image path.\n")); return E_FAIL; } // set the view's image path pView->SetImagePath(tszImagePath); // create bitmap from path LPDIRECT3DSURFACE8 lpSurf3D = ui.m_uig.GetSurface3D(); CBitmap *pbm = CBitmap::CreateViaD3DX(tszImagePath, lpSurf3D); traceSTR(info.tszImagePath); traceHEXPTR(pbm); traceDWORD(dwOffset); free(tszImagePath); tszImagePath = NULL; if (lpSurf3D) { lpSurf3D->Release(); // Need to free the surface instance after we are done as AddRef() was called earlier. lpSurf3D = NULL; } if (!pbm) { etrace(_T("Could not create image from path.\n")); return E_FAIL; } // set the view's image assert(pbm != NULL); pView->SetImage(pbm); // setimage steals the bitmap pointer assert(pbm == NULL); // add conversion from offset to view offset_view.add(dwOffset, nView); } } } { tracescope(ts2, _T("Second Pass...\n")); for (i = 0; i < dwNumElements; i++) { DWORD dwFlags = dih.lprgImageInfoArray[i].dwFlags; if (dwFlags & DIDIFT_OVERLAY) { LPDIDEVICEIMAGEINFOW lpInfoBase = dih.lprgImageInfoArray; DWORD index = i; { tracescope(ts1, _T("AddControlInfo()...\n")); traceHEXPTR(lpInfoBase); traceDWORD(index); if (lpInfoBase == NULL) { etrace(_T("lpInfoBase NULL\n")); return E_FAIL; } DIDEVICEIMAGEINFOW &info = lpInfoBase[index]; int nViewIndex = 0; if (!offset_view.getright(info.dwViewID, nViewIndex)) { etrace(_T("Could not get view index\n")); return E_FAIL; } if (nViewIndex < 0 || nViewIndex >= ui.GetNumViews()) { etrace1(_T("Invalid view index %d\n"), nViewIndex); return E_FAIL; } CDeviceView *pView = ui.GetView(nViewIndex); if (!pView) { etrace1(_T("\n"), nViewIndex); return E_FAIL; } CDeviceControl *pControl = pView->NewControl(); int nControl = pControl->GetControlIndex(); tracescope(ts2, _T("Adding Control ")); trace4(_T("%d (info index %u) to view %d (info index %u)\n"), nControl, index, nViewIndex, info.dwViewID); traceDWORD(info.dwObjID); traceDWORD(info.dwcValidPts); traceRECT(info.rcCalloutRect); traceRECT(info.rcOverlay); traceHEX(info.dwTextAlign); traceSTR(info.tszImagePath); pControl->SetObjID(info.dwObjID); pControl->SetLinePoints(int(info.dwcValidPts), info.rgptCalloutLine); pControl->SetCalloutMaxRect(info.rcCalloutRect); pControl->SetAlignment(info.dwTextAlign); if (info.tszImagePath) { LPTSTR tszOverlayPath = AllocLPTSTR(info.tszImagePath); if (tszOverlayPath) pControl->SetOverlayPath(tszOverlayPath); free(tszOverlayPath); tszOverlayPath = NULL; } pControl->SetOverlayRect(info.rcOverlay); pControl->Init(); } } } } return S_OK; } // Enumerates the controls on the device and creates one big list // view for the device. Fails if it can't enumerate for some reason. HRESULT PopulateListView(CDeviceUI &ui) { int i; HRESULT hr = S_OK; // we must have the device interface if (!ui.m_lpDID) return E_FAIL; // create one view CDeviceView *pView = ui.NewView(); if (!pView) return E_FAIL; // enable scrolling on it pView->EnableScrolling(); // get list of controls DIDEVOBJSTRUCT os; hr = FillDIDeviceObjectStruct(os, ui.m_lpDID); if (FAILED(hr)) return hr; // if there aren't any, fail int n = os.nObjects; if (n < 1) return E_FAIL; HDC hDC = CreateCompatibleDC(NULL); CPaintHelper ph(ui.m_uig, hDC); ph.SetElement(UIE_DEVOBJ); // Initially, max width is the width needed for the Control label. TCHAR tszHeader[MAX_PATH]; RECT LabelRect = {0, 0, 0, 0}; LoadString(g_hModule, IDS_LISTHEADER_CTRL, tszHeader, MAX_PATH); DrawText(hDC, tszHeader, -1, &LabelRect, DT_LEFT|DT_NOPREFIX|DT_CALCRECT); // run through and create a text for every control to // get the sizing POINT origin = {0, 0}; SIZE max = {LabelRect.right - LabelRect.left, 0}; for (i = 0; i < n; i++) { LPTSTR tszName = AllocLPTSTR(os.pdoi[i].tszName); CDeviceViewText *pText = pView->AddText( (HFONT)ui.m_uig.GetFont(UIE_DEVOBJ), ui.m_uig.GetTextColor(UIE_DEVOBJ), ui.m_uig.GetBkColor(UIE_DEVOBJ), origin, tszName); free(tszName); if (!pText) { DeleteDC(hDC); return E_FAIL; } SIZE tsize = GetRectSize(pText->GetRect()); if (tsize.cx > max.cx) max.cx = tsize.cx; if (tsize.cy > max.cy) max.cy = tsize.cy; } // Find out if we should use one column or two columns if this is a kbd device. BOOL bUseTwoColumns = FALSE; if (LOBYTE(LOWORD(ui.m_didi.dwDevType)) == DI8DEVTYPE_KEYBOARD && ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1) - max.cx >= MINLISTVIEWCALLOUTWIDTH) bUseTwoColumns = TRUE; // Do two iterations here. First one we use two columns for keyboard. 2nd one is // run only if the header labels are clipped. In which case a single column is used. for (int iPass = 0; iPass < 2; ++iPass) { // calculate max callout height based on the two possible fonts int cmaxh = 0, ch = 2 + GetTextHeight((HFONT)ui.m_uig.GetFont(UIE_CALLOUT)), chh = 2 + GetTextHeight((HFONT)ui.m_uig.GetFont(UIE_CALLOUTHIGH)); if (ch > cmaxh) cmaxh = ch; if (chh > cmaxh) cmaxh = chh; // calculate the bigger of text/callout int h = 0; if (cmaxh > h) h = cmaxh; if (max.cy > h) h = max.cy; // calculate vertical offsets of text/callout within max spacing int to = (h - max.cy) / 2, co = (h - cmaxh) / 2; // max width for text is half of the view window if (max.cx > ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1)) max.cx = ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1); // go back through all the controls and place the text while // creating the corresponding callouts int at = 0; // Start at second row since first row is used for header. Also half row spacing for (i = 0; i < n; i++) { // reposition the text CDeviceViewText *pText = pView->GetText(i); if (!pText) { DeleteDC(hDC); return E_FAIL; } SIZE s = GetRectSize(pText->GetRect()); if (bUseTwoColumns) { int iXOffset = i & 1 ? ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1) : 0; RECT rect = {iXOffset, at + to, max.cx + iXOffset, at + to + s.cy}; // Get the rectangle that is actually used. RECT adjrect = rect; if (hDC) { DrawText(hDC, pText->GetText(), -1, &adjrect, DT_NOPREFIX|DT_CALCRECT); // If the rect actually used is smaller than the space available, use the smaller rect and align to right. if (adjrect.right < rect.right) rect.left += rect.right - adjrect.right; } pText->SetRect(rect); } else { RECT rect = {0, at + to, max.cx /*> ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1) ? ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1) : max.cx*/, at + to + s.cy}; pText->SetRect(rect); } // create the control CDeviceControl *pControl = pView->NewControl(); if (!pControl) { DeleteDC(hDC); return E_FAIL; } // position it RECT rect = {max.cx + 10, at, (g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1, at + h}; // If single column, extend callout all the way to right end of view window if (!bUseTwoColumns) rect.right = g_sizeImage.cx - DEFAULTVIEWSBWIDTH; // If this is a keyboard, move to right column on odd numbered controls. if (bUseTwoColumns && (i & 1)) { rect.left += (g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1; rect.right = g_sizeImage.cx - DEFAULTVIEWSBWIDTH; } pControl->SetCalloutMaxRect(rect); // align it pControl->SetAlignment(CAF_LEFT); // set approp offset pControl->SetObjID(os.pdoi[i].dwType); // init it pControl->Init(); // go to next y coord // If this is a keyboard, then only increase y when we are moving to even numbered controls. if (!bUseTwoColumns || (i & 1)) at += h; } // Compute the rectangles for header labels if (pView->CalculateHeaderRect() && iPass == 0) { pView->RemoveAll(); bUseTwoColumns = FALSE; // Re-calculate the rects using single column. } else break; // Break out from 2nd iteration } DeleteDC(hDC); // make selection/thumb images (just for kicks) pView->MakeMissingImages(); // calculate view dimensions (for scrolling) pView->CalcDimensions(); return S_OK; } // Creates a single view with an error message. Should not fail. HRESULT PopulateErrorView(CDeviceUI &ui) { // create the new view CDeviceView *pView = ui.NewView(); if (!pView) return E_FAIL; // add text objects containing error message pView->AddWrappedLineOfText( (HFONT)ui.m_uig.GetFont(UIE_ERRORHEADER), ui.m_uig.GetTextColor(UIE_ERRORHEADER), ui.m_uig.GetBkColor(UIE_ERRORHEADER), _T("Error!")); pView->AddWrappedLineOfText( (HFONT)ui.m_uig.GetFont(UIE_ERRORMESSAGE), ui.m_uig.GetTextColor(UIE_ERRORMESSAGE), ui.m_uig.GetBkColor(UIE_ERRORMESSAGE), _T("Could not create views for device.")); pView->MakeMissingImages(); return S_OK; }