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.

754 lines
23 KiB

  1. // Copyright (C) Microsoft Corporation 1996, All Rights reserved.
  2. #include "header.h"
  3. #include "popup.h"
  4. #include "cinput.h"
  5. #include "hha_strtable.h"
  6. #include "strtable.h"
  7. #include "hhshell.h" // g_hwndApi.
  8. #include "resource.h"
  9. /////////////////////////////////////////////////////////////////////
  10. //
  11. // Constants
  12. //
  13. static const char txtComment[] = ".comment";
  14. static const char txtTopicID[] = ".topic";
  15. static const char txtCrLf[] = "\r\n";
  16. static const char txtSpace[] = " ";
  17. static const char txtDefaultFileName[] = "/cshelp.txt" ;
  18. const int TEXT_PADDING = 5; // padding around the text.
  19. const int SHADOW_WIDTH = 6;
  20. const int SHADOW_HEIGHT = 6;
  21. /////////////////////////////////////////////////////////////////////
  22. //
  23. // Globals
  24. //
  25. CPopupWindow* g_pPopupWindow;
  26. /////////////////////////////////////////////////////////////////////
  27. //
  28. // Constructor
  29. //
  30. CPopupWindow::CPopupWindow()
  31. {
  32. ZERO_INIT_CLASS(CPopupWindow);
  33. m_pfsclient = NULL; // doesn't get cleared, don't know why
  34. }
  35. /////////////////////////////////////////////////////////////////////
  36. //
  37. // Constructor
  38. //
  39. CPopupWindow::~CPopupWindow()
  40. {
  41. CleanUp();
  42. }
  43. /////////////////////////////////////////////////////////////////////
  44. //
  45. // Constructor Helper - Allows reusing window, but breaks caching.
  46. //
  47. void CPopupWindow::CleanUp(void)
  48. {
  49. if (IsValidWindow(m_hwnd))
  50. DestroyWindow(m_hwnd);
  51. if (m_pfsclient)
  52. delete m_pfsclient;
  53. if (m_pszText)
  54. lcClearFree(&m_pszText);
  55. if (m_hfont)
  56. DeleteObject(m_hfont);
  57. if (m_ptblText)
  58. delete m_ptblText;
  59. if (m_pszTextFile)
  60. lcClearFree((void**) &m_pszTextFile);
  61. m_pfsclient = NULL;
  62. m_ptblText = NULL;
  63. m_hfont = NULL;
  64. }
  65. void CPopupWindow::SetColors(COLORREF clrForeground, COLORREF clrBackground)
  66. {
  67. if (clrForeground != (COLORREF) -1)
  68. m_clrForeground = clrForeground;
  69. else
  70. m_clrForeground = GetSysColor(COLOR_WINDOWTEXT);
  71. if (clrBackground != (COLORREF) -1)
  72. m_clrBackground = clrBackground;
  73. else
  74. m_clrBackground = RGB(255, 255, 238); // dithered yellow
  75. // If the colors are the same, then use standard window colors
  76. HDC hdc = GetWindowDC(m_hwndCaller);
  77. if (GetHighContrastFlag() ||
  78. GetNearestColor(hdc, m_clrBackground) ==
  79. GetNearestColor(hdc, m_clrForeground)) {
  80. m_clrForeground = GetSysColor(COLOR_WINDOWTEXT);
  81. m_clrBackground = GetSysColor(COLOR_WINDOW);
  82. }
  83. ReleaseDC(m_hwndCaller, hdc);
  84. }
  85. // assumes text in m_pszText, result in m_rcWindow
  86. #define DEFAULT_DT_FLAGS (DT_EXPANDTABS | DT_NOCLIP | DT_NOPREFIX | DT_WORDBREAK | DT_RTLREADING)
  87. void CPopupWindow::CalculateRect(POINT pt)
  88. {
  89. RECT rc; // BUGBUG: Broken on multiple monitor systems
  90. GetClientRect(GetDesktopWindow(), &rc); // get desktop area
  91. int cyScreen = RECT_HEIGHT(rc);
  92. int cxScreen = RECT_WIDTH(rc);
  93. HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
  94. HFONT hfontOld;
  95. if (m_hfont)
  96. hfontOld = (HFONT) SelectObject(hdc, m_hfont);
  97. DrawText(hdc, m_pszText, -1, &rc, DEFAULT_DT_FLAGS | DT_CALCRECT);
  98. // Check for an overly wide but short popup
  99. if (rc.bottom * 12 < rc.right) {
  100. rc.right = rc.bottom * 12;
  101. DrawText(hdc, m_pszText, -1, &rc, DEFAULT_DT_FLAGS | DT_CALCRECT);
  102. }
  103. if (m_hfont)
  104. SelectObject(hdc, hfontOld);
  105. m_rcWindow.left = pt.x - (RECT_WIDTH(rc) / 2);
  106. m_rcWindow.right = m_rcWindow.left + RECT_WIDTH(rc);
  107. m_rcWindow.top = pt.y;
  108. m_rcWindow.bottom = m_rcWindow.top + RECT_HEIGHT(rc);
  109. m_rcWindow.left -= m_rcMargin.left;
  110. m_rcWindow.top -= m_rcMargin.top;
  111. m_rcWindow.right += m_rcMargin.right;
  112. m_rcWindow.bottom += m_rcMargin.bottom;
  113. if (m_rcWindow.left < 0)
  114. OffsetRect(&m_rcWindow, -m_rcWindow.left, 0);
  115. if (m_rcWindow.bottom > cyScreen)
  116. OffsetRect(&m_rcWindow, 0, cyScreen - m_rcWindow.bottom);
  117. }
  118. static BOOL s_fRegistered;
  119. const char txtPopupClass[] = "hh_popup";
  120. HWND CPopupWindow::doPopupWindow(void)
  121. {
  122. if (!s_fRegistered) {
  123. WNDCLASS wndclass;
  124. ZeroMemory(&wndclass, sizeof(WNDCLASS));
  125. wndclass.style = CS_VREDRAW | CS_HREDRAW;
  126. wndclass.lpfnWndProc = PopupWndProc;
  127. wndclass.hInstance = _Module.GetModuleInstance();
  128. wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  129. wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  130. wndclass.lpszClassName = txtPopupClass;
  131. s_fRegistered = RegisterClass(&wndclass);
  132. }
  133. ASSERT_COMMENT(m_clrForeground != (COLORREF) -1, "Forgot to call SetColors()");
  134. char pszPopupTitle[128];
  135. lstrcpyn(pszPopupTitle, m_pszText, 128);
  136. // t-jzybur 4-3-99: Added WS_EX_TOOLWINDOW to prevent a taskbar entry for the
  137. // popup text.
  138. m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, txtPopupClass, pszPopupTitle, WS_POPUP,
  139. m_rcWindow.left, m_rcWindow.top, RECT_WIDTH(m_rcWindow) + SHADOW_WIDTH,
  140. RECT_HEIGHT(m_rcWindow) + SHADOW_HEIGHT,
  141. m_hwndCaller, NULL, _Module.GetModuleInstance(), NULL);
  142. if (IsValidWindow(m_hwnd)) {
  143. SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR) this);
  144. ShowWindow(m_hwnd, SW_SHOW);
  145. // t-jzybur 4-3-99: Added SetForegroundWindow to activate the popup text. It could
  146. // be inactive if the previous popup text was closed by clicking the mouse somwhere
  147. // outside of the popup window.
  148. SetForegroundWindow(m_hwnd);
  149. // t-jzybur 4-3-99: Instead of capturing the focus and responding to click events,
  150. // we'll respond to click events and deactivate messages. Its a cleaneer event model,
  151. // and we won't have the possibility of locking in the hour glass cursor.
  152. // SetCapture(m_hwnd);
  153. }
  154. return m_hwnd;
  155. }
  156. LRESULT CALLBACK PopupWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  157. {
  158. HDC hdc;
  159. CPopupWindow* pThis;
  160. RECT rc;
  161. PAINTSTRUCT ps;
  162. HFONT hfontOld;
  163. switch (msg) {
  164. case WM_ERASEBKGND:
  165. hdc = (HDC) wParam;
  166. pThis = (CPopupWindow*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
  167. GetClipBox(hdc, &rc);
  168. return PaintShadowBackground(hwnd, (HDC) wParam, pThis->m_clrBackground);
  169. break;
  170. case WM_PAINT:
  171. pThis = (CPopupWindow*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
  172. hdc = BeginPaint(hwnd, &ps);
  173. GetClientRect(hwnd, &rc);
  174. rc.left += pThis->m_rcMargin.left;
  175. rc.top += pThis->m_rcMargin.top;
  176. rc.right -= pThis->m_rcMargin.right;
  177. rc.bottom -= pThis->m_rcMargin.bottom;
  178. rc.right -= SHADOW_WIDTH;
  179. rc.bottom -= SHADOW_HEIGHT;
  180. if (pThis->m_hfont)
  181. hfontOld = (HFONT) SelectObject(hdc, pThis->m_hfont);
  182. SetTextColor(hdc, pThis->m_clrForeground);
  183. SetBkColor(hdc, pThis->m_clrBackground);
  184. SetBkMode(hdc, TRANSPARENT);
  185. DrawText(hdc, pThis->m_pszText, -1, &rc, DEFAULT_DT_FLAGS);
  186. if (pThis->m_hfont)
  187. SelectObject(hdc, hfontOld);
  188. EndPaint(hwnd, &ps);
  189. break;
  190. // t-jzybur 4-3-99: Added WndProc handler to close popup text on
  191. // window deactivation messages.
  192. case WM_ACTIVATE:
  193. if (LOWORD(wParam) != WA_INACTIVE) break;
  194. case WM_LBUTTONDOWN:
  195. case WM_RBUTTONDOWN:
  196. case WM_SYSKEYDOWN:
  197. case WM_KEYDOWN:
  198. pThis = (CPopupWindow*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
  199. pThis->m_hwnd = NULL;
  200. PostMessage(hwnd, WM_CLOSE, 0, 0);
  201. break;
  202. // t-jzybur 4-3-99: Removed ReleaseCapture along with SetCapture.
  203. // case WM_DESTROY:
  204. // ReleaseCapture();
  205. // return DefWindowProc(hwnd, msg, wParam, lParam);
  206. default:
  207. return DefWindowProc(hwnd, msg, wParam, lParam);
  208. }
  209. return 0;
  210. }
  211. /***************************************************************************
  212. FUNCTION: PaintShadowBackground
  213. PURPOSE: Draws a border and a shadow around a window
  214. PARAMETERS:
  215. hwnd
  216. hdc
  217. RETURNS:
  218. COMMENTS:
  219. MODIFICATION DATES:
  220. 02-Mar-1997 [ralphw]
  221. ***************************************************************************/
  222. static const WORD rgwPatGray[] =
  223. { 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA };
  224. const DWORD PATMERGE = 0x00A000C9;
  225. BOOL PaintShadowBackground(HWND hwnd, HDC hdc, COLORREF clrBackground)
  226. {
  227. BOOL fStockBrush; // Whether hBrush is a stock object
  228. /*
  229. * First the background of the "fake" window is erased leaving the
  230. * desktop where the shadow will be.
  231. */
  232. RECT rcClient; // Will always be client rectangle
  233. GetClientRect(hwnd, &rcClient);
  234. RECT rct = rcClient;
  235. rct.bottom = max(0, rct.bottom - SHADOW_HEIGHT);
  236. rct.right = max(0, rct.right - SHADOW_WIDTH);
  237. HBRUSH hBrush = CreateSolidBrush((clrBackground == (COLORREF) -1 ?
  238. GetSysColor(COLOR_WINDOW) : clrBackground));
  239. if (!hBrush)
  240. return FALSE;
  241. UnrealizeObject(hBrush);
  242. POINT pt;
  243. pt.x = pt.y = 0;
  244. ClientToScreen(hwnd, &pt);
  245. SetBrushOrgEx(hdc, pt.x, pt.y, NULL);
  246. FillRect(hdc, &rct, hBrush);
  247. DeleteObject(hBrush);
  248. // Next we create the "window" border
  249. rct = rcClient;
  250. rct.bottom = max(0, rct.bottom - SHADOW_HEIGHT);
  251. rct.right = max(0, rct.right - SHADOW_WIDTH);
  252. FrameRect(hdc, &rct, (HBRUSH) GetStockObject(BLACK_BRUSH));
  253. InflateRect(&rct, -1, -1);
  254. FrameRect(hdc, &rct, (HBRUSH) GetStockObject(LTGRAY_BRUSH));
  255. // Now we create the brush for the the shadow
  256. hBrush = 0;
  257. HBITMAP hbmGray;
  258. if ((hbmGray = CreateBitmap(8, 8, 1, 1, rgwPatGray)) != NULL) {
  259. hBrush = CreatePatternBrush(hbmGray);
  260. DeleteObject(hbmGray);
  261. fStockBrush = FALSE;
  262. }
  263. // If we cannot create the pattern brush, we try to use a black brush.
  264. if (hBrush == 0) {
  265. if (!(hBrush == GetStockObject(BLACK_BRUSH)))
  266. return FALSE;
  267. fStockBrush = TRUE;
  268. }
  269. SetROP2(hdc, R2_MASKPEN);
  270. SetBkMode(hdc, TRANSPARENT);
  271. HPEN hpen;
  272. if ((hpen = (HPEN) GetStockObject(NULL_PEN)) != 0)
  273. SelectObject(hdc, hpen); // We do not care if this fails
  274. HBRUSH hbrushTemp = (HBRUSH) SelectObject(hdc, hBrush); // or if this fails, since the
  275. // paint behavior will be okay.
  276. rct = rcClient; // Paint the right side rectangle
  277. rct.top = rct.top + SHADOW_HEIGHT;
  278. rct.left = max(0, rct.right - SHADOW_WIDTH);
  279. PatBlt(hdc, rct.left, rct.top, rct.right - rct.left,
  280. rct.bottom - rct.top, PATMERGE);
  281. rct = rcClient; // Paint the bottom rectangle
  282. rct.top = max(0, rct.bottom - SHADOW_HEIGHT);
  283. rct.left = rct.left + SHADOW_WIDTH;
  284. // Note overlap by one pixel!
  285. rct.right = max(0, rct.right - SHADOW_WIDTH + 1);
  286. PatBlt(hdc, rct.left, rct.top, rct.right - rct.left,
  287. rct.bottom - rct.top, PATMERGE);
  288. // Cleanup brush
  289. if (hbrushTemp != NULL)
  290. SelectObject(hdc, hbrushTemp);
  291. if (!fStockBrush)
  292. DeleteObject(hBrush);
  293. return TRUE;
  294. }
  295. BOOL CPopupWindow::ReadTextFile(PCSTR pszFile)
  296. {
  297. // If the string pointer is NULL or empty we have to bail.
  298. if (!pszFile || pszFile[0] == '\0')
  299. return FALSE ;
  300. // Now, verify that we have a text file specified. Urg! More parsing of URL's
  301. CStr cszFileName;
  302. PCSTR pszSubFile = GetCompiledName(pszFile, &cszFileName) ;
  303. if (!pszSubFile || pszSubFile[0] == '\0')
  304. {
  305. pszSubFile = txtDefaultFileName ;
  306. }
  307. cszFileName += txtDoubleColonSep ;
  308. cszFileName += pszSubFile ;
  309. #if 0//REVIEW:: This never works, because CleanUp resets everything. Removed for safety.
  310. // Check to see if its cached.
  311. if (lstrcmpi(cszFileName, m_pszTextFile) == 0)
  312. return TRUE; // we've cached this file
  313. #endif
  314. CInput input;
  315. if (!input.Open(cszFileName))
  316. return FALSE;
  317. if (m_ptblText)
  318. delete m_ptblText;
  319. // Allocate a text buffer.
  320. CStr cszText;
  321. if (m_pszTextFile)
  322. lcFree(m_pszTextFile);
  323. m_pszTextFile = lcStrDup(cszFileName);
  324. m_ptblText = new CTable;
  325. while (input.getline(&cszText)) {
  326. if (!IsSamePrefix(cszText, txtComment))
  327. m_ptblText->AddString(cszText);
  328. }
  329. return TRUE;
  330. }
  331. //////////////////////////////////////////////////////////////////////////
  332. //
  333. // CreatePopupWindow.
  334. //
  335. HWND CPopupWindow::CreatePopupWindow(HWND hwndCaller, PCSTR pszFile,
  336. HH_POPUP* pPopup)
  337. {
  338. if (!pPopup) // TODO: Validate pPopup pointer.
  339. return NULL ;
  340. m_hwndCaller = hwndCaller;
  341. //--- Getting the string to display. We can get the string to display in three ways:
  342. // 1. From a string contained in the HH_POPUP structure.
  343. // 2. From a string resource in a module.
  344. // 3. From a txt file embedded in the CHM.
  345. // This order is the order of least complicated to most complicated. We start with the
  346. // least complicated method to save loading extra working set.
  347. // NOTE: A future possibility would be to search in the reverse order. This would allow
  348. // using a string in the HH_POPUP structure if one wasn't found in the embedded txt file.
  349. bool bFoundString = false ;
  350. if ((pPopup->idString == 0) && IsNonEmptyString(pPopup->pszText)) // 1. Get string from HH_POPUP. Only if idString is 0! See HH 3532.
  351. {
  352. m_pszText = lcStrDup(pPopup->pszText);
  353. bFoundString = true ;
  354. }
  355. else if (pPopup->idString && pPopup->hinst) // 2. From a string resource in a module.
  356. {
  357. m_pszText = (PSTR) lcMalloc(MAX_STRING_RESOURCE_LEN);
  358. char *pszText = NULL;
  359. if ((pszText =(char *) GetStringResource(pPopup->idString, pPopup->hinst)) && *pszText )
  360. {
  361. strcpy(m_pszText,pszText);
  362. bFoundString = true ;
  363. }
  364. }
  365. else if (IsNonEmptyString(pszFile)) // 3. From a txt file embedded in the CHM.
  366. {
  367. // Try to read the text file.
  368. if (ReadTextFile(pszFile))
  369. {
  370. ASSERT(m_ptblText);
  371. for (int pos = 1; pos <= m_ptblText->CountStrings(); pos++) {
  372. if (IsSamePrefix(m_ptblText->GetPointer(pos), txtTopicID)) {
  373. PSTR pszNumber = FirstNonSpace(m_ptblText->GetPointer(pos) +
  374. strlen(txtTopicID));
  375. if (pszNumber && pPopup->idString == (UINT) Atoi(pszNumber))
  376. break;
  377. }
  378. }
  379. // Do we have enough strings?
  380. if (pos <= m_ptblText->CountStrings())
  381. {
  382. CStr cszText(txtZeroLength);
  383. BOOL fAddSpace = FALSE;
  384. for (++pos; pos <= m_ptblText->CountStrings(); pos++) {
  385. PCSTR pszLine = m_ptblText->GetPointer(pos);
  386. if (*pszLine == '.')
  387. break;
  388. if (!*pszLine) {
  389. if (pos + 1 <= m_ptblText->CountStrings()) {
  390. pszLine = m_ptblText->GetPointer(pos + 1);
  391. if (*pszLine != '.')
  392. cszText += txtCrLf;
  393. }
  394. fAddSpace = FALSE;
  395. continue;
  396. }
  397. else if (fAddSpace)
  398. cszText += txtSpace;
  399. cszText += pszLine;
  400. fAddSpace = TRUE;
  401. }
  402. cszText.TransferPointer(&m_pszText);
  403. bFoundString = true ;
  404. }
  405. else
  406. {
  407. if (IsHelpAuthor(NULL))
  408. {
  409. char szMsgBuf[256];
  410. wsprintf(szMsgBuf, pGetDllStringResource(IDS_HHA_MISSING_TP_TXT),
  411. pPopup->idString, pszFile);
  412. doAuthorMsg(IDS_IDH_GENERIC_STRING, szMsgBuf);
  413. }
  414. }
  415. }
  416. else
  417. {
  418. // We couldn't read the text file in. Will display error popup...
  419. doAuthorMsg(IDS_CANT_OPEN, pszFile);
  420. }
  421. }
  422. // This needs to be true when displaying static strings loaded from the resource
  423. // because the font specified by the user might not be appropriate for the
  424. // string loaded from the resource.
  425. //
  426. BOOL bUseDefaultFont = FALSE;
  427. //--- Do we have a string?
  428. if (!bFoundString)
  429. {
  430. if (m_pszText)
  431. {
  432. lcClearFree(&m_pszText);
  433. m_pszText = NULL ;
  434. }
  435. m_pszText = (PSTR) lcMalloc(MAX_STRING_RESOURCE_LEN);
  436. char *pszText;
  437. if ((pszText = (char *) GetStringResource(IDS_IDH_MISSING_CONTEXT)) )
  438. {
  439. strcpy(m_pszText,pszText);
  440. bUseDefaultFont = TRUE;
  441. }
  442. else
  443. {
  444. // Dang it! We can't even get our own string.
  445. CleanUp() ;
  446. return NULL ;
  447. }
  448. }
  449. //--- Okay, now we can display the string.
  450. m_rcMargin.left = (pPopup->rcMargins.left >= 0 ?
  451. pPopup->rcMargins.left : TEXT_PADDING);
  452. m_rcMargin.top = (pPopup->rcMargins.top >= 0 ?
  453. pPopup->rcMargins.top : TEXT_PADDING);
  454. m_rcMargin.right = (pPopup->rcMargins.right >= 0 ?
  455. pPopup->rcMargins.right : TEXT_PADDING);
  456. m_rcMargin.bottom = (pPopup->rcMargins.bottom >= 0 ?
  457. pPopup->rcMargins.bottom : TEXT_PADDING);
  458. if (IsNonEmptyString(pPopup->pszFont) && !bUseDefaultFont) {
  459. if (m_hfont)
  460. DeleteObject(m_hfont);
  461. m_hfont = CreateUserFont(pPopup->pszFont);
  462. }
  463. else if (!m_hfont)
  464. m_hfont = CreateUserFont(GetStringResource(IDS_DEFAULT_RES_FONT));
  465. // Get a default location to display.
  466. POINT pt = pPopup->pt;
  467. if (pt.x == -1 && pt.x == -1 && IsWindow(hwndCaller))
  468. {
  469. RECT rcWindow;
  470. GetWindowRect(hwndCaller, &rcWindow);
  471. pt.x = rcWindow.left + (RECT_WIDTH(rcWindow) / 2);
  472. pt.y = rcWindow.top;
  473. }
  474. CalculateRect(pt);
  475. SetColors(pPopup->clrForeground, pPopup->clrBackground);
  476. return doPopupWindow();
  477. }
  478. //////////////////////////////////////////////////////////////////////////
  479. //
  480. // Handle the HH_TP_HELP_CONTEXTMENU command. Display the What's this menu.
  481. //
  482. HWND
  483. doTpHelpContextMenu(HWND hwndMain, LPCSTR pszFile, DWORD_PTR ulData)
  484. {
  485. /*
  486. In WinHelp we put up a little menu for this message. In HTML Help we don't.
  487. So we remove the menu and just handle this like HH_TP_HELP_WM_HELP.
  488. */
  489. return doTpHelpWmHelp(hwndMain, pszFile, ulData) ;
  490. /*
  491. ASSERT(IsWindow(hwndMain)) ;
  492. // Create the menu.
  493. HMENU hMenu = LoadMenu(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_WHATSTHIS_MENU)) ;
  494. ASSERT(hMenu) ;
  495. // Get the Popup Menu
  496. HMENU hPopupMenu = GetSubMenu(hMenu, 0) ;
  497. //--- Get the location to display the menu
  498. POINT pt ;
  499. // Use the mouse cursor position.
  500. GetCursorPos(&pt) ;
  501. // Set the style of the menu.
  502. DWORD style = TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD ;
  503. // Display the menu.
  504. int iCmd = TrackPopupMenuEx(hPopupMenu,
  505. style ,
  506. pt.x, pt.y,
  507. g_hwndApi ? g_hwndApi : hwndMain, // We have to have a window in the current thread!
  508. NULL) ;
  509. #ifdef _DEBUG
  510. DWORD err ;
  511. if (iCmd == 0)
  512. {
  513. err = ::GetLastError() ;
  514. }
  515. #endif
  516. // Cleanup
  517. DestroyMenu(hMenu) ;
  518. // Act on the item.
  519. if (iCmd == IDM_WHATSTHIS)
  520. {
  521. return doTpHelpWmHelp(hwndMain, pszFile, ulData) ;
  522. }
  523. else
  524. {
  525. return NULL ;
  526. }
  527. */
  528. }
  529. ///////////////// Dialog control parsing from helpcall.c in user32 ///////
  530. const int MAX_ATTEMPTS = 5; // maximum -1 id controls to search through
  531. HWND doTpHelpWmHelp(HWND hwndMain, LPCSTR pszFile, DWORD_PTR ulData)
  532. {
  533. int id = GetDlgCtrlID(hwndMain); // get control id
  534. int idSave = id;
  535. DWORD* pid = (DWORD*) ulData;
  536. if ((short) id == -1)
  537. { // static control?
  538. HWND hwndCtrl = hwndMain;
  539. int cAttempts = 0;
  540. // For non-id controls (typically static controls), step
  541. // through to the next tab item. Keep finding the next tab
  542. // item until we find a valid id, or we have tried
  543. // MAX_ATTEMPTS times.
  544. do
  545. {
  546. hwndCtrl = GetNextWindow(hwndCtrl, GW_HWNDNEXT);
  547. // hwndCtrl will be NULL if hwndMain doesn't have a parent,
  548. // or if there are no tab stops.
  549. if (!hwndCtrl)
  550. {
  551. DBWIN("GetNextDlgHelpItem failed.");
  552. return NULL;
  553. }
  554. id = GetDlgCtrlID(hwndCtrl);
  555. }
  556. while ((id == -1) && (++cAttempts < MAX_ATTEMPTS));
  557. }
  558. // Find the id value in array of id/help context values
  559. for (int i = 0; pid[i]; i += 2)
  560. {
  561. if ((int) pid[i] == id)
  562. break;
  563. }
  564. // Create a popup structure to pass to doDisplayTextPopup.
  565. HH_POPUP popup ;
  566. memset(&popup, 0, sizeof(popup)) ;
  567. // We want the default window size.
  568. popup.pt.x = -1 ;
  569. popup.pt.y = -1 ;
  570. // We want the default margins.
  571. popup.rcMargins.top =
  572. popup.rcMargins.bottom =
  573. popup.rcMargins.left =
  574. popup.rcMargins.right = -1 ;
  575. if (!pid[i])
  576. {
  577. popup.hinst = _Module.GetResourceInstance();
  578. switch (id) {
  579. case IDOK:
  580. popup.idString = IDS_IDH_OK;
  581. break;
  582. case IDCANCEL:
  583. popup.idString = IDS_IDH_CANCEL;
  584. break;
  585. case IDHELP:
  586. popup.idString = IDS_IDH_HELP;
  587. break;
  588. default:
  589. if (IsHelpAuthor(NULL))
  590. {
  591. char szMsgBuf[256];
  592. wsprintf(szMsgBuf,
  593. pGetDllStringResource(IDS_HHA_MISSING_HELP_ID), idSave);
  594. doAuthorMsg(IDS_IDH_GENERIC_STRING, szMsgBuf);
  595. }
  596. popup.idString = IDS_IDH_MISSING_CONTEXT;
  597. break;
  598. }
  599. return doDisplayTextPopup(hwndMain, NULL, &popup) ;
  600. }
  601. else
  602. {
  603. ulData = pid[i + 1];
  604. if (ulData == (DWORD) -1)
  605. return NULL; // caller doesn't want help after all
  606. if (IsHelpAuthor(NULL))
  607. {
  608. char szMsgBuf[256];
  609. wsprintf(szMsgBuf, pGetDllStringResource(IDS_HHA_HELP_ID),
  610. (int) pid[i], (int) pid[i + 1], pszFile);
  611. SendStringToParent(szMsgBuf);
  612. }
  613. // Set the id of the string that we want.
  614. popup.idString = (UINT)ulData;
  615. return doDisplayTextPopup(hwndMain, pszFile, &popup) ;
  616. }
  617. }
  618. /////////////////////////////////////////////////////////////////////
  619. //
  620. // doDisplaytextPopup
  621. //
  622. HWND
  623. doDisplayTextPopup(HWND hwndMain, LPCSTR pszFile, HH_POPUP* pPopup)
  624. {
  625. if (!g_pPopupWindow)
  626. {
  627. g_pPopupWindow = new CPopupWindow;
  628. }
  629. g_pPopupWindow->CleanUp();
  630. return g_pPopupWindow->CreatePopupWindow(hwndMain, pszFile, pPopup);
  631. }