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.

2235 lines
70 KiB

  1. // Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved.
  2. #include "header.h"
  3. #include "hhctrl.h"
  4. #include "strtable.h"
  5. #include "resource.h"
  6. #include "hha_strtable.h"
  7. #include "onclick.h"
  8. #include "index.h"
  9. #include "toc.h"
  10. #include "wwheel.h"
  11. #include "web.h"
  12. #include <shellapi.h>
  13. #include <wininet.h>
  14. #include "sample.h"
  15. #include "subset.h"
  16. #include "secwin.h" //For extern CHHWinType** pahwnd;
  17. #undef WINUSERAPI
  18. #define WINUSERAPI
  19. #include "htmlhelp.h"
  20. #include "lasterr.h" // Support for reporting the last error.
  21. #define NOTEXT_BTN_HEIGHT 12
  22. #define NOTEXT_BTN_WIDTH 12
  23. #define CXBUTTONEXTRA 8 // spacing between text and button
  24. #define CYBUTTONEXTRA 8
  25. static const char txtOpen[] = "open";
  26. static const char txtCplOpen[] = "cplopen";
  27. static const char txtRegSection[] = "Software\\Microsoft\\HtmlHelp\\";
  28. static const char txtShortcut[] = "shortcut";
  29. static DWORD GetTextDimensions(HWND hwnd, PCSTR psz, HFONT hfont = NULL);
  30. HRESULT OSLangMatchesChm(CExCollection *pCollection);
  31. HRESULT GetWordWheelHits( PSTR pszKeyword, CWordWheel* pWordWheel,
  32. CTable* ptblURLs, CWTable* ptblTitles,
  33. CWTable *ptblLocations, BOOL bTestMode,
  34. BOOL bSkipCurrent, BOOL bFullURL = FALSE );
  35. HRESULT GetWordWheelHits( CExCollection* pCollection,
  36. CTable* ptblItems, CTable* ptblURLs,
  37. CWTable* ptblTitles, CWTable *ptblLocations,
  38. BOOL bKLink, BOOL bTestMode, BOOL bSkipCurrent );
  39. HRESULT OnWordWheelLookup( CTable* ptblItems, CExCollection* pExCollection,
  40. PCSTR pszDefaultTopic = NULL, POINT* ppt = NULL,
  41. HWND hWndParent = NULL, BOOL bDialog = TRUE,
  42. BOOL bKLink = TRUE,
  43. BOOL bTestMode = FALSE, BOOL bSkipCurrent = FALSE,
  44. BOOL bAlwaysShowList = FALSE, BOOL bAlphaSortHits = TRUE,
  45. PCSTR pszWindow = NULL);
  46. BOOL g_HackForBug_HtmlHelpDB_1884 = 0;
  47. /***************************************************************************
  48. FUNCTION: OnWordWheelLookup
  49. PURPOSE: Given a keyword, or semi-colon delimited list of keywords,
  50. find the hits. If no hits found display a "not found" message;
  51. if one hit found then just jump to the topic, otherwise
  52. display a list of topics for the user to choose from.
  53. PARAMETERS:
  54. pszKeywords - word(s) to lookup
  55. ... the rest the same as OnWordWheelLookup( CTable* ... )
  56. RETURNS:
  57. TRUE if there is at least one match. FALSE otherwise.
  58. COMMENTS:
  59. No support for external titles with this API.
  60. MODIFICATION DATES:
  61. 09-Jan-1998 [paulti]
  62. ***************************************************************************/
  63. HRESULT OnWordWheelLookup( PSTR pszKeywords, CExCollection* pExCollection,
  64. PCSTR pszDefaultTopic, POINT* ppt,
  65. HWND hWndParent, BOOL bDialog, BOOL bKLink,
  66. BOOL bTestMode, BOOL bSkipCurrent,
  67. BOOL bAlwaysShowList, BOOL bAlphaSortHits,
  68. PCSTR pszWindow)
  69. {
  70. // trim leading and trailing spaces
  71. char* pszKeywords2 = new char[strlen(pszKeywords)+1];
  72. strcpy( pszKeywords2, pszKeywords );
  73. SzTrimSz( pszKeywords2 );
  74. // create our lists
  75. CTable tblItems;
  76. // initialize our item list
  77. tblItems.AddString( "" ); // set item1 to NULL -- no external titles
  78. PSTR pszKeyword = StrToken( pszKeywords2, ";" );
  79. while( pszKeyword ) {
  80. CHAR szKeyword[HHWW_MAX_KEYWORD_LENGTH+1];
  81. lstrcpyn( szKeyword, pszKeyword, sizeof(szKeyword)-1 );
  82. SzTrimSz( szKeyword );
  83. tblItems.AddString( szKeyword );
  84. pszKeyword = StrToken(NULL, ";");
  85. }
  86. delete [] pszKeywords2;
  87. return OnWordWheelLookup( &tblItems, pExCollection,
  88. pszDefaultTopic, ppt, hWndParent, bDialog,
  89. bKLink, bTestMode, bSkipCurrent,
  90. bAlwaysShowList, bAlphaSortHits, pszWindow );
  91. }
  92. /***************************************************************************
  93. FUNCTION: OnWordWheelLookup
  94. PURPOSE: Given a list of keywords find the hits. If no hits found
  95. display a "not found" message if one hit found then just
  96. jump to the topic, otherwise display a list of topics for
  97. the user to choose from.
  98. PARAMETERS:
  99. ptblItems - word(s) to lookup (first item is semi-colon delimited
  100. list of external titles to check).
  101. pCollection - collection pointer, needed to access word wheels.
  102. pszDefaultTopic - default topic to jump to.
  103. ppt - pointer to a point to center the window on. If NULL
  104. then we will use the current mouse pointer position.
  105. hWndParent - window to parent the "Topics Found" dialog/menu to.
  106. If NULL, then use the active window.
  107. bDialog - dialog based? If not, use a menu.
  108. bKLink - is this a keyword lookup? if not, use the ALink list.
  109. bTestMode - test existence only (dont' show a UI).
  110. bSkipCurrent - skip the current URL in the returned list.
  111. bAlwaysShowList - always show the hit list even if only one topic is found.
  112. bAlphaSortHits - alpha sort the title list or not.
  113. RETURNS:
  114. TRUE if there is at least one match. FALSE otherwise.
  115. COMMENTS:
  116. MODIFICATION DATES:
  117. 09-Jan-1998 [paulti]
  118. ***************************************************************************/
  119. HRESULT OnWordWheelLookup( CTable* ptblItems, CExCollection* pCollection,
  120. PCSTR pszDefaultTopic, POINT* ppt,
  121. HWND hWndParent, BOOL bDialog, BOOL bKLink,
  122. BOOL bTestMode, BOOL bSkipCurrent,
  123. BOOL bAlwaysShowList, BOOL bAlphaSortHits, PCSTR pszWindow)
  124. {
  125. HRESULT hr = S_OK;
  126. UINT CodePage = pCollection ? pCollection->GetMasterTitle()->GetInfo()->GetCodePage() : CP_ACP;
  127. // create our lists
  128. CWTable tblTitles( CodePage );
  129. CWTable tblLocations( CodePage );
  130. CTable tblURLs;
  131. if( pCollection ) {
  132. // get the active window if non specified
  133. if( !hWndParent )
  134. hWndParent = GetActiveWindow();
  135. // get current mouse pointer position if non-specified
  136. POINT pt;
  137. if( !ppt ) {
  138. GetCursorPos( &pt );
  139. ppt = &pt;
  140. #if 1 // reverted bug fix #5516
  141. HWND hwnd = GetFocus();
  142. if ( hwnd ) {
  143. DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE );
  144. if ( dwStyle & BS_NOTIFY )
  145. {
  146. RECT rc;
  147. if( GetWindowRect(hwnd, &rc) ) {
  148. pt.y = rc.top+(RECT_WIDTH(rc)/2);
  149. pt.x = rc.left + ((RECT_HEIGHT(rc)/3)*2); // two-thirds of the height of the button
  150. }
  151. }
  152. }
  153. #endif
  154. }
  155. hr = GetWordWheelHits( pCollection,
  156. ptblItems, &tblURLs, &tblTitles, &tblLocations,
  157. bKLink, bTestMode, bSkipCurrent );
  158. }
  159. else {
  160. hr = HH_E_KEYWORD_NOT_SUPPORTED;
  161. }
  162. // we are all done if we are just in test mode
  163. if( bTestMode )
  164. return hr;
  165. int iIndex = 0;
  166. char szURL[INTERNET_MAX_URL_LENGTH];
  167. // if we get no topics then display the default message
  168. // othewise an "error" message
  169. if( FAILED(hr) || tblURLs.CountStrings() < 1) {
  170. if( pCollection && pszDefaultTopic ) {
  171. if( pCollection && StrRChr( pszDefaultTopic, ':' ) == NULL ) {
  172. CStr szCurrentURL;
  173. GetCurrentURL( &szCurrentURL, hWndParent );
  174. CStr szFileName;
  175. LPSTR pszColon = StrRChr( szCurrentURL.psz, ':' );
  176. LPSTR pszSlash = StrRChr( szCurrentURL.psz, '/' );
  177. LPSTR pszTail = max( pszColon, pszSlash );
  178. lstrcpyn( szURL, szCurrentURL.psz, (int)(pszTail - szCurrentURL.psz +2) );
  179. strcat( szURL, pszDefaultTopic );
  180. }
  181. else
  182. strcpy( szURL, pszDefaultTopic );
  183. hr = S_OK; // set to S_OK so the jump below will work
  184. }
  185. else {
  186. int iStr = 0;
  187. switch( hr ) {
  188. case HH_E_KEYWORD_NOT_FOUND:
  189. iStr = IDS_HH_E_KEYWORD_NOT_FOUND;
  190. break;
  191. case HH_E_KEYWORD_IS_PLACEHOLDER:
  192. iStr = IDS_HH_E_KEYWORD_IS_PLACEHOLDER;
  193. break;
  194. case HH_E_KEYWORD_NOT_IN_SUBSET:
  195. iStr = IDS_HH_E_KEYWORD_NOT_IN_SUBSET;
  196. break;
  197. case HH_E_KEYWORD_NOT_IN_INFOTYPE:
  198. iStr = IDS_HH_E_KEYWORD_NOT_IN_INFOTYPE;
  199. break;
  200. case HH_E_KEYWORD_EXCLUDED:
  201. iStr = IDS_HH_E_KEYWORD_EXCLUDED;
  202. break;
  203. case HH_E_KEYWORD_NOT_SUPPORTED:
  204. iStr = IDS_REQUIRES_HTMLHELP;
  205. break;
  206. default:
  207. iStr = IDS_IDH_MISSING_CONTEXT;
  208. break;
  209. }
  210. MsgBox(iStr, MB_OK | MB_ICONWARNING | MB_SETFOREGROUND);
  211. }
  212. }
  213. else {
  214. // if only one topic then jump to it
  215. if( !bAlwaysShowList && tblURLs.CountStrings() == 1 ) {
  216. tblURLs.GetString( szURL, 1 );
  217. }
  218. else {
  219. // we can sort the title table since it contains the index value
  220. // of the associated URL so just make sure to always fetch the
  221. // URL index from the selected title string and use that to get the URL
  222. if( bAlphaSortHits ) {
  223. tblTitles.SetSorting(GetSystemDefaultLCID());
  224. tblTitles.SortTable(sizeof(HASH));
  225. }
  226. if( !bDialog && tblURLs.CountStrings() < 20 ) {
  227. HMENU hMenu = CreatePopupMenu();
  228. if( hMenu ) {
  229. for( int i = 1; i <= tblURLs.CountStrings(); i++ ) {
  230. LPSTR psz = tblTitles.GetHashStringPointer(i);
  231. // if title too long, truncate it (511 seems like a good max)
  232. if( psz && *psz ) {
  233. int iLen = (int)strlen(psz);
  234. #define MAX_LEN 511
  235. char sz[MAX_LEN+1];
  236. if( iLen >= MAX_LEN ) {
  237. strncpy( sz, psz, MAX_LEN-1 );
  238. sz[MAX_LEN] = 0;
  239. psz = sz;
  240. }
  241. }
  242. HxAppendMenu(hMenu, MF_STRING, IDM_RELATED_TOPIC + i, psz );
  243. }
  244. int iCmd = TrackPopupMenu(hMenu,
  245. TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON |
  246. TPM_NONOTIFY | TPM_RETURNCMD,
  247. ppt->x, ppt->y, 0, hWndParent, NULL);
  248. DestroyMenu( hMenu );
  249. if( iCmd ) {
  250. iIndex = tblTitles.GetInt( iCmd - IDM_RELATED_TOPIC );
  251. tblURLs.GetString( szURL, iIndex );
  252. }
  253. else {
  254. hr = HH_E_KEYWORD_NOT_FOUND; // Means we have nothing to jump to
  255. }
  256. }
  257. }
  258. else
  259. {
  260. HFONT hfont;
  261. if ( pCollection )
  262. hfont = pCollection->m_phmData->GetContentFont();
  263. else
  264. hfont = _Resource.GetUIFont(); // Not ideal but will have to do.
  265. UINT CodePage = pCollection ? pCollection->GetMasterTitle()->GetInfo()->GetCodePage() : CP_ACP;
  266. CTopicList TopicList( hWndParent, &tblTitles, hfont, &tblLocations );
  267. if( TopicList.DoModal() ) {
  268. iIndex = tblTitles.GetInt( TopicList.m_pos );
  269. tblURLs.GetString( szURL, iIndex );
  270. }
  271. else {
  272. hr = HH_E_KEYWORD_NOT_FOUND; // Means we have nothing to jump to
  273. }
  274. }
  275. }
  276. }
  277. // if we found something to jump to then jump to it
  278. if( !FAILED(hr) )
  279. {
  280. if (pCollection && pszWindow && pszWindow[0])
  281. {
  282. CStr cszPrefix = szURL;
  283. // Is the window we are attempting to open defined by the master CHM?
  284. CHHWinType* phh = FindWindowType(pszWindow, NULL, pCollection->GetPathName());
  285. // Does this window actually exist?
  286. if (phh && IsWindow(phh->hwndHelp))
  287. {
  288. doHHWindowJump(cszPrefix, phh->hwndHelp);
  289. return hr;
  290. }
  291. cszPrefix += ">";
  292. cszPrefix += pszWindow;
  293. OnDisplayTopic(hWndParent, cszPrefix, 0);
  294. }
  295. else
  296. doHHWindowJump( szURL, hWndParent );
  297. }
  298. return hr;
  299. }
  300. /***************************************************************************
  301. FUNCTION: GetWordWheelHits
  302. PURPOSE: Given a single keyword, find the hits
  303. Return S_OK if there is at least one found hit
  304. PARAMETERS:
  305. pWordWheel - word wheel to look in
  306. ptblURLS - URL list
  307. ptblTitles - title list
  308. bTestMode - test existence only (replaces old TestAKLink code)
  309. bSkipCurrent - skip the current URL in the returned list
  310. bFullURL - return a URL with a full pathname to the title or (default)
  311. just return the URL with just the filename of the title
  312. RETURNS:
  313. S_OK - hits were found.
  314. HH_E_KEYWORD_NOT_FOUND - no hits found.
  315. HH_E_KEYWORD_IS_PLACEHOLDER - keyword is a placeholder or
  316. a "runaway" see also.
  317. HH_E_KEYWORD_NOT_IN_SUBSET - no hits found due to subset
  318. exclusion.
  319. HH_E_KEYWORD_NOT_IN_INFOTYPE - no hits found due to infotype
  320. exclusion.
  321. HH_E_KEYWORD_EXCLUDED - no hits found due to infotype
  322. and subset exclusion.
  323. HH_E_KEYWORD_NOT_SUPPORTED - keywords not supported in this mode.
  324. COMMENTS:
  325. HH_E_KEYWORD_EXCLUDED is returned only when no hits are found due to
  326. *both* removal by subsetting and infotype
  327. MODIFICATION DATES:
  328. 13-Apr-1998 [paulti]
  329. ***************************************************************************/
  330. HRESULT GetWordWheelHits( PSTR pszKeyword, CWordWheel* pWordWheel,
  331. CTable* ptblURLs, CWTable* ptblTitles, CWTable *ptblLocations,
  332. BOOL bTestMode, BOOL bSkipCurrent, BOOL bFullURL )
  333. {
  334. HRESULT hr = S_OK;
  335. BOOL bExcludedBySubset = FALSE;
  336. BOOL bExcludedByInfoType = FALSE;
  337. BOOL bPlaceHolder = FALSE;
  338. CStructuralSubset* pSubset;
  339. if( pszKeyword ) {
  340. DWORD dwIndexLast = HHWW_ERROR;
  341. DWORD dwIndexFirst = pWordWheel->GetIndex( pszKeyword, FALSE, &dwIndexLast );
  342. if( dwIndexFirst == HHWW_ERROR )
  343. return HH_E_KEYWORD_NOT_FOUND;
  344. for( DWORD dwIndex = dwIndexFirst; dwIndex <= dwIndexLast; dwIndex++ ) {
  345. // skip over the placeholders
  346. if( pWordWheel->IsPlaceHolder( dwIndex ) ) {
  347. bPlaceHolder = TRUE;
  348. continue;
  349. }
  350. // follow the see also links
  351. CHAR szSeeAlso[HHWW_MAX_KEYWORD_LENGTH+1];
  352. if( pWordWheel->GetSeeAlso( dwIndex, szSeeAlso, sizeof(szSeeAlso) ) ) {
  353. if( pWordWheel->AddRef() >= HHWW_MAX_LEVELS ) {
  354. pWordWheel->Release();
  355. return HH_E_KEYWORD_IS_PLACEHOLDER;
  356. }
  357. hr = GetWordWheelHits( szSeeAlso, pWordWheel, ptblURLs, ptblTitles, ptblLocations,
  358. bTestMode, bSkipCurrent, bFullURL );
  359. pWordWheel->Release();
  360. switch( hr ) {
  361. case HH_E_KEYWORD_EXCLUDED:
  362. bExcludedBySubset = TRUE;
  363. bExcludedByInfoType = TRUE;
  364. break;
  365. case HH_E_KEYWORD_NOT_IN_SUBSET:
  366. bExcludedBySubset = TRUE;
  367. break;
  368. case HH_E_KEYWORD_NOT_IN_INFOTYPE:
  369. bExcludedByInfoType = TRUE;
  370. break;
  371. case HH_E_KEYWORD_IS_PLACEHOLDER:
  372. bPlaceHolder = TRUE;
  373. break;
  374. }
  375. continue;
  376. }
  377. // fetch the hits
  378. CStr cszCurrentURL;
  379. GetCurrentURL( &cszCurrentURL );
  380. DWORD dwHitCount = pWordWheel->GetHitCount(dwIndex);
  381. if (dwHitCount != HHWW_ERROR) {
  382. for (DWORD i = 0; i < dwHitCount; i++) {
  383. CExTitle* pTitle = NULL;
  384. DWORD dwURLId = pWordWheel->GetHit(dwIndex, i, &pTitle);
  385. if (pTitle && dwURLId != HHWW_ERROR) {
  386. #if 0 // we do not support infotypes currently
  387. CSubSet* pSS;
  388. const unsigned int *pdwITBits;
  389. //
  390. // Do we need to filter based on infotypes ?
  391. //
  392. if ( pTitle->m_pCollection && pTitle->m_pCollection->m_pSubSets &&
  393. (pSS = pTitle->m_pCollection->m_pSubSets->GetIndexSubset()) && !pSS->m_bIsEntireCollection )
  394. {
  395. //
  396. // Yep, do the filter thang.
  397. //
  398. pdwITBits = pTitle->GetTopicITBits(dwURLId);
  399. if (! pTitle->m_pCollection->m_pSubSets->fIndexFilter(pdwITBits) ) {
  400. bExcludedByInfoType = TRUE;
  401. continue;
  402. }
  403. }
  404. #endif
  405. //
  406. // Do we need to filter based on structural subsets?
  407. //
  408. if( pTitle->m_pCollection && pTitle->m_pCollection->m_pSSList &&
  409. (pSubset = pTitle->m_pCollection->m_pSSList->GetF1()) && !pSubset->IsEntire() )
  410. {
  411. // Yes, filter using the current structural subset for F1.
  412. //
  413. if (! pSubset->IsTitleInSubset(pTitle) ) {
  414. bExcludedBySubset = TRUE;
  415. continue;
  416. }
  417. }
  418. // if we make it this far and we are in test mode
  419. // we can bail out and return S_OK
  420. if( bTestMode )
  421. return S_OK;
  422. char szTitle[1024];
  423. szTitle[0] = 0;
  424. pTitle->GetTopicName( dwURLId, szTitle, sizeof(szTitle) );
  425. if( !szTitle[0] )
  426. strcpy( szTitle, GetStringResource( IDS_UNTITLED ) );
  427. char szLocation[INTERNET_MAX_PATH_LENGTH]; // 2048 should be plenty
  428. szLocation[0] = 0;
  429. if( pTitle->GetTopicLocation(dwURLId, szLocation, INTERNET_MAX_PATH_LENGTH) != S_OK )
  430. strcpy( szLocation, GetStringResource( IDS_UNKNOWN ) );
  431. char szURL[INTERNET_MAX_URL_LENGTH];
  432. szURL[0] = 0;
  433. pTitle->GetTopicURL( dwURLId, szURL, sizeof(szURL), bFullURL );
  434. char szFullURL[INTERNET_MAX_URL_LENGTH];
  435. szFullURL[0] = 0;
  436. pTitle->ConvertURL( szURL, szFullURL );
  437. if( szURL[0] ) {
  438. if( !ptblURLs->IsStringInTable(szURL) ) {
  439. if( cszCurrentURL.IsEmpty()
  440. || !(bSkipCurrent && (lstrcmpi( cszCurrentURL, szURL ) == 0) ||(lstrcmpi( cszCurrentURL, szFullURL ) == 0 ) )) {
  441. int iIndex = ptblURLs->AddString(szURL);
  442. ptblTitles->AddIntAndString(iIndex, szTitle[0]?szTitle:"");
  443. ptblLocations->AddString( *szLocation?szLocation:"" );
  444. }
  445. }
  446. }
  447. }
  448. }
  449. }
  450. }
  451. }
  452. // determine the proper return value
  453. if( ptblURLs->CountStrings() < 1 ) {
  454. if( bExcludedBySubset && bExcludedByInfoType )
  455. hr = HH_E_KEYWORD_EXCLUDED;
  456. else if( bExcludedBySubset )
  457. hr = HH_E_KEYWORD_NOT_IN_SUBSET;
  458. else if( bExcludedByInfoType )
  459. hr = HH_E_KEYWORD_NOT_IN_INFOTYPE;
  460. else if( bPlaceHolder )
  461. hr = HH_E_KEYWORD_IS_PLACEHOLDER;
  462. else
  463. hr = HH_E_KEYWORD_NOT_FOUND;
  464. }
  465. else
  466. hr = S_OK;
  467. return hr;
  468. }
  469. /***************************************************************************
  470. FUNCTION: GetWordWheelHits
  471. PURPOSE: Get all the links for the specified keywords
  472. Return TRUE if there is at least one match
  473. PARAMETERS:
  474. pCollection - point to the collection
  475. ptblItems - item table, item 1 is the list of external titles
  476. and items 2 thru N are the list of keywords
  477. ptblURLs - URL list
  478. ptblTitles - title list
  479. bKLink - is this a klink (defaults to alink)
  480. bTestMode - test existence only (replaces old TestAKLink code)
  481. bSkipCurrent - skip the current URL in the returned list
  482. RETURNS:
  483. COMMENTS:
  484. MODIFICATION DATES:
  485. 14-Nov-1997 [paulti]
  486. ***************************************************************************/
  487. HRESULT GetWordWheelHits( CExCollection* pCollection,
  488. CTable* ptblItems, CTable* ptblURLs, CWTable* ptblTitles, CWTable *ptblLocations,
  489. BOOL bKLink, BOOL bTestMode, BOOL bSkipCurrent )
  490. {
  491. int pos;
  492. CWordWheel* pWordWheel = NULL;
  493. HRESULT hr = S_OK;
  494. HRESULT hrReturn = HH_E_KEYWORD_NOT_FOUND; // assume the worst
  495. if( pCollection ) {
  496. pWordWheel = (bKLink ? pCollection->m_pDatabase->GetKeywordLinks() :
  497. pCollection->m_pDatabase->GetAssociativeLinks());
  498. }
  499. // if we did not get a word wheel pointer then skip the internal
  500. // word wheels and fall back to just the external ones
  501. if( pWordWheel ) {
  502. // add in the internal hits first
  503. for( pos = 2; pos <= ptblItems->CountStrings(); pos++ ) {
  504. CStr cszKeywords(ptblItems->GetPointer(pos)); // copy so StrToken can modify
  505. PSTR pszKeyword = StrToken(cszKeywords, ";");
  506. while( pszKeyword ) {
  507. hr = GetWordWheelHits( pszKeyword, pWordWheel,
  508. ptblURLs, ptblTitles, ptblLocations,
  509. bTestMode, bSkipCurrent, TRUE );
  510. if( bTestMode && !FAILED(hr) )
  511. return hr;
  512. // if we failed again, then collate the resultant error
  513. if( FAILED( hrReturn ) && FAILED( hr ) ) {
  514. switch( hrReturn ) {
  515. case HH_E_KEYWORD_NOT_IN_INFOTYPE:
  516. if( hr == HH_E_KEYWORD_NOT_IN_SUBSET )
  517. hrReturn = HH_E_KEYWORD_EXCLUDED;
  518. else
  519. hrReturn = hr;
  520. break;
  521. case HH_E_KEYWORD_NOT_IN_SUBSET:
  522. if( hr == HH_E_KEYWORD_NOT_IN_INFOTYPE )
  523. hrReturn = HH_E_KEYWORD_EXCLUDED;
  524. else
  525. hrReturn = hr;
  526. break;
  527. case HH_E_KEYWORD_EXCLUDED:
  528. hrReturn = HH_E_KEYWORD_EXCLUDED;
  529. break;
  530. default:
  531. hrReturn = hr;
  532. break;
  533. }
  534. }
  535. else if( !FAILED( hr ) )
  536. hrReturn = hr;
  537. pszKeyword = StrToken(NULL, ";");
  538. if( pszKeyword )
  539. pszKeyword = FirstNonSpace(pszKeyword);
  540. }
  541. }
  542. }
  543. // create a list of the external titles
  544. // skip those titles that are already in the collection
  545. CStr cszTitle(ptblItems->GetPointer(1));
  546. if( !IsEmptyString(cszTitle) ) {
  547. PSTR pszTitle = StrToken(cszTitle, ";");
  548. CWTable* ptblTitleFiles = new CWTable(ptblTitles->GetCodePage()); // REVIEW: external titles must have the same codepage!
  549. while( pszTitle ) {
  550. if( *pszTitle ) {
  551. TCHAR szTitle[MAX_PATH];
  552. PSTR pszTitle2 = szTitle;
  553. strcpy( szTitle, pszTitle );
  554. pszTitle2 = FirstNonSpace( szTitle );
  555. RemoveTrailingSpaces( pszTitle2 );
  556. CExTitle* pTitle = NULL;
  557. if( *pszTitle2 && pCollection && FAILED(pCollection->URL2ExTitle(pszTitle2,&pTitle ) ) ) {
  558. CStr Title;
  559. // check if the file lives where the master file lives
  560. if( pszTitle2 ) {
  561. char szPathName[_MAX_PATH];
  562. char szFileName[_MAX_FNAME];
  563. char szExtension[_MAX_EXT];
  564. SplitPath((LPSTR)pszTitle2, NULL, NULL, szFileName, szExtension);
  565. char szMasterPath[_MAX_PATH];
  566. char szMasterDrive[_MAX_DRIVE];
  567. SplitPath((LPSTR)pCollection->m_csFile, szMasterDrive, szMasterPath, NULL, NULL);
  568. strcpy( szPathName, szMasterDrive );
  569. CatPath( szPathName, szMasterPath );
  570. CatPath( szPathName, szFileName );
  571. strcat( szPathName, szExtension );
  572. Title = szPathName;
  573. if( (GetFileAttributes(szPathName) != HFILE_ERROR) || FindThisFile( NULL, pszTitle2, &Title, FALSE ) ) {
  574. if( Title.IsNonEmpty() )
  575. ptblTitleFiles->AddString( Title );
  576. }
  577. }
  578. }
  579. }
  580. pszTitle = StrToken(NULL, ";");
  581. }
  582. // add in the external hits last
  583. int iTitleCount = ptblTitleFiles->CountStrings();
  584. for( int iTitle = 1; iTitle <= iTitleCount; iTitle++ ) {
  585. char szTitle[MAX_PATH];
  586. ptblTitleFiles->GetString( szTitle, iTitle );
  587. // get title objects
  588. CExTitle* pTitle = new CExTitle( szTitle, NULL );
  589. CTitleDatabase* pDatabase = new CTitleDatabase( pTitle );
  590. CWordWheel* pWordWheel = NULL;
  591. if( bKLink )
  592. pWordWheel = pDatabase->GetKeywordLinks();
  593. else
  594. pWordWheel = pDatabase->GetAssociativeLinks();
  595. for (int pos = 2; pos <= ptblItems->CountStrings(); pos++) {
  596. CStr cszKeywords(ptblItems->GetPointer(pos)); // copy so StrToken can modify
  597. PSTR pszKeyword = StrToken(cszKeywords, ";");
  598. while( pszKeyword ) {
  599. hr = GetWordWheelHits( pszKeyword, pWordWheel,
  600. ptblURLs, ptblTitles, ptblLocations,
  601. bTestMode, bSkipCurrent, TRUE );
  602. if( bTestMode && !FAILED(hr) ) {
  603. hrReturn = hr;
  604. break;
  605. }
  606. // if we failed again, then collate the resultant error
  607. if( FAILED( hrReturn ) && FAILED( hr ) ) {
  608. switch( hrReturn ) {
  609. case HH_E_KEYWORD_NOT_IN_INFOTYPE:
  610. if( hr == HH_E_KEYWORD_NOT_IN_SUBSET )
  611. hrReturn = HH_E_KEYWORD_EXCLUDED;
  612. else
  613. hrReturn = hr;
  614. break;
  615. case HH_E_KEYWORD_NOT_IN_SUBSET:
  616. if( hr == HH_E_KEYWORD_NOT_IN_INFOTYPE )
  617. hrReturn = HH_E_KEYWORD_EXCLUDED;
  618. else
  619. hrReturn = hr;
  620. break;
  621. case HH_E_KEYWORD_EXCLUDED:
  622. hrReturn = HH_E_KEYWORD_EXCLUDED;
  623. break;
  624. default:
  625. hrReturn = hr;
  626. break;
  627. }
  628. }
  629. else if( !FAILED( hr ) )
  630. hrReturn = hr;
  631. pszKeyword = StrToken(NULL, ";");
  632. if( pszKeyword )
  633. pszKeyword = FirstNonSpace(pszKeyword);
  634. }
  635. if( bTestMode && !FAILED(hr) ) {
  636. hrReturn = hr;
  637. break;
  638. }
  639. }
  640. // free title objects
  641. delete pDatabase;
  642. delete pTitle;
  643. if( bTestMode && !FAILED(hr) ) {
  644. hrReturn = hr;
  645. break;
  646. }
  647. }
  648. // free our title list
  649. delete ptblTitleFiles;
  650. }
  651. return hrReturn;
  652. }
  653. /***************************************************************************
  654. FUNCTION: CHtmlHelpControl::OnAKLink
  655. PURPOSE: Return TRUE if there is at least one match
  656. PARAMETERS:
  657. fKLink - is this a klink? (defaults to alink)
  658. bTestMode - test for existence only
  659. RETURNS:
  660. COMMENTS:
  661. MODIFICATION DATES:
  662. 28-JAN-1998 [paulti] major rewrite
  663. ***************************************************************************/
  664. BOOL CHtmlHelpControl::OnAKLink( BOOL fKLink, BOOL bTestMode )
  665. {
  666. if( !m_ptblItems )
  667. return FALSE;
  668. // get our cursor position first since the merge prompt
  669. // may change our current position
  670. //
  671. // BUGBUG: what if the use tabbed to this link and then pressed ENTER?
  672. // We should then anchor the menu to the bottom-left of the parent
  673. // window. Right?
  674. POINT pt;
  675. GetCursorPos(&pt);
  676. HWND hwnd = GetFocus();
  677. if ( hwnd ) {
  678. DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE );
  679. if ( dwStyle & BS_NOTIFY )
  680. {
  681. RECT rc;
  682. if( GetWindowRect(hwnd, &rc) ) {
  683. pt.y = rc.top+((rc.bottom-rc.top)/2);
  684. pt.x = rc.left + ((RECT_HEIGHT(rc)/3)*2); // two-thirds of the height of the button
  685. }
  686. }
  687. }
  688. // get the parent window
  689. HWND hWndParent = NULL ;
  690. if( m_fButton && m_hwndDisplayButton )
  691. hWndParent = m_hwndDisplayButton;
  692. else if( m_hwnd && IsWindow(m_hwnd) )
  693. hWndParent = m_hwnd;
  694. else
  695. {
  696. // Tunnel through IE to get the HWND of HTML Help's frame window.
  697. hWndParent = GetHtmlHelpFrameWindow() ;
  698. }
  699. // If nothing else works, try the actice window. Eck!
  700. if( !hWndParent )
  701. hWndParent = GetActiveWindow();
  702. // Worse, try the desktop window!
  703. if( !hWndParent )
  704. hWndParent = GetDesktopWindow();
  705. //
  706. // <mc> Find a CExCollection pointer...
  707. //
  708. CExCollection* pExCollection = NULL;
  709. CStr cstr;
  710. if ( m_pWebBrowserApp )
  711. {
  712. m_pWebBrowserApp->GetLocationURL(&cstr);
  713. pExCollection = GetCurrentCollection(NULL, (PCSTR)cstr);
  714. }
  715. // should we always display the jump list even on one hit?
  716. BOOL bAlwaysShowList = FALSE;
  717. if( m_flags[0] == 1 )
  718. bAlwaysShowList = TRUE;
  719. // call our shared "Topics Found" handler
  720. BOOL fPopupMenu = m_fPopupMenu;
  721. if (fPopupMenu == TRUE && OSLangMatchesChm(pExCollection) != S_OK)
  722. fPopupMenu = FALSE;
  723. HRESULT hr = OnWordWheelLookup( m_ptblItems, pExCollection, m_pszDefaultTopic, &pt, hWndParent,
  724. !fPopupMenu, fKLink, bTestMode, TRUE, bAlwaysShowList, TRUE, m_pszWindow);
  725. if( FAILED( hr ) )
  726. return FALSE;
  727. return TRUE;
  728. }
  729. LRESULT CHtmlHelpControl::StaticTextControlSubWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  730. {
  731. HDC hDC;
  732. RECT rc;
  733. CHtmlHelpControl* pThis = (CHtmlHelpControl*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  734. switch (msg)
  735. {
  736. case WM_KILLFOCUS:
  737. case WM_SETFOCUS:
  738. if ( pThis && pThis->m_imgType == IMG_TEXT )
  739. {
  740. hDC = ::GetDC(hwnd);
  741. GetClientRect(hwnd, &rc);
  742. ::DrawFocusRect(hDC, &rc);
  743. ReleaseDC(hwnd, hDC);
  744. return 0;
  745. }
  746. break;
  747. case WM_KEYDOWN:
  748. if ( wParam == VK_RETURN || (wParam == VK_SPACE && (pThis->m_imgType == IMG_TEXT)) )
  749. {
  750. PostMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(IDBTN_DISPLAY, BN_CLICKED), (LPARAM)hwnd);
  751. return 0;
  752. }
  753. break;
  754. }
  755. if ( pThis )
  756. return CallWindowProc(pThis->m_lpfnlStaticTextControlWndProc, hwnd, msg, wParam, lParam);
  757. else
  758. return 0;
  759. }
  760. BOOL CHtmlHelpControl::CreateOnClickButton(void)
  761. {
  762. // First create the window, then size it to match text and/or bitmap
  763. PSTR pszClassName;
  764. char szWindowText[MAX_PATH];
  765. if ( m_imgType == IMG_BUTTON )
  766. {
  767. pszClassName = "button";
  768. WideCharToMultiByte( m_CodePage, 0, m_pwszButtonText, -1, szWindowText, sizeof(szWindowText), NULL, NULL );
  769. }
  770. else
  771. {
  772. pszClassName = "static";
  773. *szWindowText = '\0';
  774. }
  775. m_hwndDisplayButton = CreateWindowEx(0, pszClassName, szWindowText, WS_CHILD | m_flags[1] | WS_VISIBLE | BS_NOTIFY,
  776. 0, 0, NOTEXT_BTN_WIDTH, NOTEXT_BTN_WIDTH, m_hwnd, (HMENU) IDBTN_DISPLAY,
  777. _Module.GetModuleInstance(), NULL);
  778. if (!m_hwndDisplayButton)
  779. return FALSE;
  780. //
  781. // <mc>
  782. // I'm subclassing the static text controls only for the purpose of implementing proper
  783. // focus and UI activation. I have to do this because I don't have any other way to be notified
  784. // of loss and acquisition of focus. A much better way to do this would be to implement these
  785. // controls as window-less.
  786. // </mc>
  787. //
  788. // 4/27/98 - <mc> Changed to also subclass buttons so we can have enter key support.
  789. //
  790. m_lpfnlStaticTextControlWndProc = (WNDPROC)GetWindowLongPtr(m_hwndDisplayButton, GWLP_WNDPROC);
  791. SetWindowLongPtr(m_hwndDisplayButton, GWLP_USERDATA, (LONG_PTR)this);
  792. SetWindowLongPtr(m_hwndDisplayButton, GWLP_WNDPROC, (LONG_PTR)StaticTextControlSubWndProc);
  793. if (m_pszBitmap) {
  794. char szBitmap[MAX_PATH];
  795. BOOL m_fBuiltInImage = (IsSamePrefix(m_pszBitmap, txtShortcut, -2));
  796. if (!m_fBuiltInImage) {
  797. if (!ConvertToCacheFile(m_pszBitmap, szBitmap)) {
  798. AuthorMsg(IDS_CANT_OPEN, m_pszBitmap);
  799. // REVIEW: better default?
  800. if (m_fIcon)
  801. goto NoImage;
  802. m_hImage = LoadBitmap(_Module.GetResourceInstance(), txtShortcut);
  803. goto GotImage;
  804. }
  805. }
  806. if (m_fBuiltInImage)
  807. m_hImage = LoadBitmap(_Module.GetResourceInstance(), m_pszBitmap);
  808. else
  809. m_hImage = LoadImage(_Module.GetResourceInstance(), szBitmap,
  810. (m_fIcon ? IMAGE_ICON : IMAGE_BITMAP), 0, 0,
  811. LR_LOADFROMFILE);
  812. if (!m_hImage) {
  813. AuthorMsg(IDS_CANT_OPEN, m_pszBitmap);
  814. // REVIEW: we should use a default bitmap
  815. goto NoImage;
  816. }
  817. GotImage:
  818. if (m_fIcon) {
  819. // We use IMAGE_ICON for both cursors and icons. Internally,
  820. // the only significant difference is that a cursor could be
  821. // forced to monochrome if we used the IMAGE_CURSOR command.
  822. if (m_imgType == IMG_BUTTON) {
  823. // REVIEW: should check actual ICON/CURSOR size
  824. MoveWindow(m_hwndDisplayButton, 0, 0,
  825. 32 + CXBUTTONEXTRA,
  826. 32 + CYBUTTONEXTRA, FALSE);
  827. SendMessage(m_hwndDisplayButton, BM_SETIMAGE,
  828. IMAGE_ICON, (LPARAM) m_hImage);
  829. }
  830. else
  831. SendMessage(m_hwndDisplayButton, STM_SETIMAGE, IMAGE_CURSOR,
  832. (LPARAM) m_hImage);
  833. }
  834. else {
  835. if (m_imgType == IMG_BUTTON) {
  836. BITMAP bmp;
  837. GetObject(m_hImage, sizeof(bmp), &bmp);
  838. MoveWindow(m_hwndDisplayButton, 0, 0,
  839. bmp.bmWidth + CXBUTTONEXTRA,
  840. bmp.bmHeight + CYBUTTONEXTRA, FALSE);
  841. SendMessage(m_hwndDisplayButton, BM_SETIMAGE,
  842. IMAGE_BITMAP, (LPARAM) m_hImage);
  843. }
  844. }
  845. }
  846. else
  847. SendMessage(m_hwndDisplayButton, WM_SETFONT,
  848. m_hfont ? (WPARAM) m_hfont : (WPARAM) _Resource.GetUIFont(), FALSE);
  849. NoImage:
  850. if ( m_pwszButtonText && *m_pwszButtonText )
  851. {
  852. HDC hdc = GetDC(m_hwndDisplayButton);
  853. if (hdc == NULL)
  854. return FALSE;
  855. HFONT hfontOld = (HFONT) SelectObject(hdc,
  856. m_hfont ? m_hfont : _Resource.GetUIFont());
  857. SIZE size;
  858. IntlGetTextExtentPoint32W(hdc, m_pwszButtonText, lstrlenW(m_pwszButtonText), &size);
  859. SelectObject(hdc, hfontOld);
  860. ReleaseDC(m_hwndDisplayButton, hdc);
  861. if (m_imgType == IMG_TEXT)
  862. MoveWindow(m_hwndDisplayButton, 0, 0, size.cx, size.cy, FALSE);
  863. else
  864. MoveWindow(m_hwndDisplayButton, 0, 0, size.cx + CXBUTTONEXTRA,
  865. size.cy + CYBUTTONEXTRA, FALSE);
  866. }
  867. GetWindowRect(m_hwndDisplayButton, &m_rcButton);
  868. // REVIEW: will this set ALL static windows to use this cursor?
  869. // change the cursor to the hand icon
  870. if (m_imgType == IMG_TEXT) {
  871. SetClassLongPtr(m_hwndDisplayButton, GCLP_HCURSOR,
  872. (LONG_PTR) LoadCursor(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDCUR_HAND)));
  873. }
  874. // set the text color -- default to visited link color if not specified
  875. if( m_imgType == IMG_TEXT ) {
  876. if( m_clrFont == CLR_INVALID )
  877. m_clrFont = m_clrFontLink;
  878. }
  879. // enable/disable dynalinks
  880. if( (m_action == ACT_KLINK || m_action == ACT_ALINK) && m_flags[2] == 1 ) {
  881. if( !OnAKLink((m_action == ACT_KLINK),TRUE) ) {
  882. EnableWindow( m_hwndDisplayButton, FALSE ); // disable the window
  883. if( m_imgType == IMG_TEXT )
  884. m_clrFont = m_clrFontDisabled;
  885. }
  886. }
  887. return TRUE;
  888. }
  889. /***************************************************************************
  890. FUNCTION: GetTextDimensions
  891. PURPOSE:
  892. PARAMETERS:
  893. hwnd
  894. psz
  895. hfont -- may be NULL
  896. RETURNS:
  897. COMMENTS:
  898. If hfont is NULL, font in the window's DC will be used
  899. MODIFICATION DATES:
  900. 01-Sep-1997 [ralphw]
  901. ***************************************************************************/
  902. static DWORD GetTextDimensions(HWND hwnd, PCSTR psz, HFONT hfont)
  903. {
  904. HDC hdc = GetDC(hwnd);
  905. if (hdc == NULL)
  906. return 0L;
  907. HFONT hfontOld;
  908. if (hfont)
  909. hfontOld = (HFONT) SelectObject(hdc, hfont);
  910. SIZE size;
  911. GetTextExtentPoint(hdc, psz, (int)strlen(psz), &size);
  912. DWORD dwRet = MAKELONG(size.cx, size.cy) +
  913. MAKELONG(CXBUTTONEXTRA, CYBUTTONEXTRA);
  914. if (hfont)
  915. SelectObject(hdc, hfontOld);
  916. ReleaseDC(hwnd, hdc);
  917. return dwRet;
  918. }
  919. // undocumented WinHelp API commands
  920. #define HELP_HASH 0x095 // Jump to file and topic based on hash
  921. #define HELP_HASH_POPUP 0x096 // Put up glossary based on hash
  922. #define MAX_WINHELP 247
  923. void STDCALL CHtmlHelpControl::OnClick(void)
  924. {
  925. switch (m_action) {
  926. case ACT_ABOUT_BOX:
  927. {
  928. CAboutBox aboutbox(this);
  929. aboutbox.DoModal();
  930. }
  931. break;
  932. case ACT_HHCTRL_VERSION:
  933. ModalDialog(TRUE);
  934. MsgBox(IDS_VERSION);
  935. ModalDialog(FALSE);
  936. break;
  937. case ACT_RELATED_TOPICS:
  938. {
  939. if (m_pSiteMap->Count() == 0) // don't allow zero items
  940. break;
  941. CExCollection* pExCollection = NULL;
  942. CStr cstr;
  943. if ( m_pWebBrowserApp )
  944. {
  945. m_pWebBrowserApp->GetLocationURL(&cstr);
  946. pExCollection = GetCurrentCollection(NULL, (PCSTR)cstr);
  947. }
  948. if (m_pSiteMap->Count() == 1 && !m_flags[0])
  949. {
  950. SITEMAP_ENTRY *pSiteMapEntry = m_pSiteMap->GetSiteMapEntry(1);
  951. if(pSiteMapEntry)
  952. JumpToUrl(pSiteMapEntry, m_pSiteMap);
  953. }
  954. else if (m_fPopupMenu && OSLangMatchesChm(pExCollection) == S_OK)
  955. OnRelatedMenu();
  956. else
  957. {
  958. UINT CodePage = GetCodePage();
  959. CWTable tblTitles( CodePage );
  960. TCHAR szURL[INTERNET_MAX_URL_LENGTH];
  961. for (int i = 0; i < m_pSiteMap->Count(); i++)
  962. {
  963. strcpy(szURL, m_pSiteMap->GetSiteMapEntry(i+1)->pszText );
  964. tblTitles.AddIntAndString(i+1, szURL);
  965. }
  966. CTopicList TopicList(this, &tblTitles, m_hfont);
  967. if (TopicList.DoModal() > 0) {
  968. int iIndex = tblTitles.GetInt( TopicList.m_pos );
  969. JumpToUrl(m_pSiteMap->GetSiteMapEntry(iIndex), m_pSiteMap);
  970. }
  971. }
  972. }
  973. break;
  974. case ACT_WINHELP:
  975. if (!m_ptblItems || m_ptblItems->CountStrings() < 1) {
  976. AuthorMsg(IDS_ACT_WINHELP_NO_HELP);
  977. break;
  978. }
  979. if (m_ptblItems->CountStrings() < 2) {
  980. AuthorMsg(IDS_ACT_WINHELP_NO_ID);
  981. break;
  982. }
  983. // Note that we don't track this, and therefore don't force
  984. // the help file closed.
  985. {
  986. PSTR pc = m_ptblItems->GetPointer(1);
  987. PSTR pcMax = NULL;
  988. if ( strlen( pc ) > MAX_WINHELP )
  989. {
  990. pcMax = new char[MAX_WINHELP];
  991. strncpy( pcMax, pc, MAX_WINHELP-1 );
  992. pcMax[MAX_WINHELP-1] = 0;
  993. }
  994. else
  995. pcMax = pc;
  996. ::WinHelp(m_hwnd, pcMax,
  997. IsDigit(*(m_ptblItems->GetPointer(2))) ?
  998. (m_fWinHelpPopup ? HELP_CONTEXTPOPUP : HELP_CONTEXT) :
  999. (m_fWinHelpPopup ? HELP_HASH_POPUP : HELP_HASH),
  1000. IsDigit(*(m_ptblItems->GetPointer(2))) ?
  1001. Atoi(m_ptblItems->GetPointer(2)) :
  1002. WinHelpHashFromSz(m_ptblItems->GetPointer(2)));
  1003. if ( pcMax != pc )
  1004. delete [] pcMax;
  1005. }
  1006. if ( m_fWinHelpPopup )
  1007. g_HackForBug_HtmlHelpDB_1884 = 1;
  1008. break;
  1009. case ACT_SHORTCUT:
  1010. // don't allow if running in IE
  1011. if( !GetCurrentCollection(NULL, (PCSTR)NULL) ) {
  1012. HWND hWndParent;
  1013. if (!IsValidWindow(m_hwnd)) // in case we are windowless
  1014. hWndParent = FindTopLevelWindow(GetActiveWindow());
  1015. else
  1016. hWndParent = FindTopLevelWindow(GetParent(m_hwnd));
  1017. char szMsg[1024];
  1018. strcpy( szMsg, GetStringResource( IDS_REQUIRES_HTMLHELP ) );
  1019. MessageBox( hWndParent, szMsg, _Resource.MsgBoxTitle(),
  1020. MB_OK | MB_ICONWARNING | MB_SETFOREGROUND );
  1021. break;
  1022. }
  1023. if (m_ptblItems && m_ptblItems->CountStrings()) {
  1024. ShortCut(this, m_ptblItems->GetPointer(1),
  1025. (m_ptblItems->CountStrings() > 1 ?
  1026. m_ptblItems->GetPointer(2) : ""),
  1027. m_hwndParent);
  1028. }
  1029. else
  1030. AuthorMsg(IDS_SHORTCUT_ARGULESS);
  1031. break;
  1032. case ACT_HHWIN_PRINT:
  1033. case ACT_CLOSE:
  1034. case ACT_MAXIMIZE:
  1035. case ACT_MINIMIZE:
  1036. {
  1037. HWND hwndParent;
  1038. if (!IsValidWindow(m_hwnd)) // in case we are windowless
  1039. hwndParent = FindTopLevelWindow(GetActiveWindow());
  1040. else
  1041. hwndParent = FindTopLevelWindow(GetParent(m_hwnd));
  1042. switch (m_action) {
  1043. case ACT_CLOSE:
  1044. PostMessage(hwndParent, WM_CLOSE, 0, 0);
  1045. return;
  1046. case ACT_MINIMIZE:
  1047. ShowWindow(hwndParent, SW_MINIMIZE);
  1048. return;
  1049. case ACT_MAXIMIZE:
  1050. ShowWindow(hwndParent,
  1051. IsZoomed(hwndParent) ? SW_RESTORE : SW_SHOWMAXIMIZED);
  1052. return;
  1053. case ACT_HHWIN_PRINT:
  1054. {
  1055. char szClass[256];
  1056. GetClassName(hwndParent, szClass, sizeof(szClass));
  1057. if (IsSamePrefix(szClass, txtHtmlHelpWindowClass, -2)) {
  1058. PostMessage(hwndParent, WM_COMMAND, IDTB_PRINT, 0);
  1059. }
  1060. }
  1061. break;
  1062. }
  1063. }
  1064. break;
  1065. case ACT_TCARD:
  1066. {
  1067. if (IsEmptyString(m_pszActionData))
  1068. break; // REVIEW: nag the help author
  1069. WPARAM wParam = Atoi(m_pszActionData);
  1070. LPARAM lParam = 0;
  1071. PCSTR psz = StrChr(m_pszActionData, ',');
  1072. if (psz) {
  1073. psz = FirstNonSpace(psz + 1);
  1074. if (IsDigit(*psz))
  1075. lParam = Atoi(psz);
  1076. else
  1077. lParam = (LPARAM) psz;
  1078. }
  1079. HWND hwndParent;
  1080. if (!IsValidWindow(m_hwnd)) // in case we are windowless
  1081. hwndParent = FindTopLevelWindow(GetActiveWindow());
  1082. else
  1083. hwndParent = FindTopLevelWindow(GetParent(m_hwnd));
  1084. if (hwndParent)
  1085. SendMessage(hwndParent, WM_TCARD, wParam, lParam);
  1086. }
  1087. break;
  1088. case ACT_KLINK:
  1089. OnAKLink(TRUE);
  1090. break;
  1091. case ACT_ALINK:
  1092. OnAKLink(FALSE);
  1093. break;
  1094. case ACT_SAMPLE:
  1095. if(!OnCopySample())
  1096. MsgBox(IDS_SAMPLE_ERROR);
  1097. break;
  1098. default:
  1099. // REVIEW: nag the help author
  1100. break;
  1101. }
  1102. }
  1103. /***************************************************************************
  1104. FUNCTION: OSLangMatchesChm()
  1105. PURPOSE: Checks lang of os verses the lang of this title
  1106. PARAMETERS:
  1107. RETURNS: S_OK, S_FALSE
  1108. MODIFICATION DATES:
  1109. 11-Nov-1998
  1110. ***************************************************************************/
  1111. HRESULT OSLangMatchesChm(CExCollection *pCollection)
  1112. {
  1113. if (pCollection == NULL)
  1114. {
  1115. return S_OK;
  1116. }
  1117. CTitleInformation *pInfo = pCollection->GetMasterTitle()->GetInfo();
  1118. LANGID MasterLangId;
  1119. if (pInfo)
  1120. MasterLangId = LANGIDFROMLCID(pInfo->GetLanguage());
  1121. else
  1122. return S_FALSE;
  1123. CLanguage cLang;
  1124. if (cLang.GetUiLanguage() == MasterLangId)
  1125. return S_OK;
  1126. return S_FALSE;
  1127. }
  1128. /***************************************************************************
  1129. FUNCTION: HashFromSz
  1130. PURPOSE: Convert a string into a WinHelp hash number
  1131. PARAMETERS:
  1132. pszKey -- string to convert
  1133. RETURNS: WinHelp-compatible hash number
  1134. COMMENTS:
  1135. This is the same algorithm that WinHelp and Help Workshop uses. The
  1136. result can be used to jump to a topic in a help file.
  1137. MODIFICATION DATES:
  1138. 14-Jun-1997 [ralphw]
  1139. ***************************************************************************/
  1140. static const HASH MAX_CHARS = 43L;
  1141. extern "C" HASH WinHelpHashFromSz(PCSTR pszKey)
  1142. {
  1143. HASH hash = 0;
  1144. int cch = (int)strlen(pszKey);
  1145. // REVIEW: 14-Oct-1993 [ralphw] -- Note lack of check for a hash collision.
  1146. for (int ich = 0; ich < cch; ++ich) {
  1147. if (pszKey[ich] == '!')
  1148. hash = (hash * MAX_CHARS) + 11;
  1149. else if (pszKey[ich] == '.')
  1150. hash = (hash * MAX_CHARS) + 12;
  1151. else if (pszKey[ich] == '_')
  1152. hash = (hash * MAX_CHARS) + 13;
  1153. else if (pszKey[ich] == '0')
  1154. hash = (hash * MAX_CHARS) + 10;
  1155. else if (pszKey[ich] <= 'Z')
  1156. hash = (hash * MAX_CHARS) + (pszKey[ich] - '0');
  1157. else
  1158. hash = (hash * MAX_CHARS) + (pszKey[ich] - '0' - ('a' - 'A'));
  1159. }
  1160. /*
  1161. * Since the value 0 is reserved as a nil value, if any topic id
  1162. * actually hashes to this value, we just move it.
  1163. */
  1164. return (hash == 0 ? 0 + 1 : hash);
  1165. }
  1166. VOID (WINAPI* pSHHelpShortcuts_RunDLL)(HWND hwndStub, HINSTANCE hAppInstance, LPCSTR lpszCmdLine, int nCmdShow);
  1167. static const char txtShellShortCut[] = "shell32.dll,SHHelpShortcuts_RunDLL";
  1168. static const char txtShell32Dll[] = "shell32.dll";
  1169. BOOL ShortCut(CHtmlHelpControl* phhctrl, LPCSTR pszString1, LPCSTR pszString2,
  1170. HWND hwndMsgOwner)
  1171. {
  1172. HWND hwndApp;
  1173. HINSTANCE hinstRet;
  1174. CHourGlass hourglass;
  1175. // Make a copy so we can modify it
  1176. if (IsEmptyString(pszString1))
  1177. return FALSE;
  1178. CStr csz(pszString1);
  1179. PSTR pszComma = StrChr(csz.psz, ',');
  1180. if (!pszComma) {
  1181. AuthorMsg(IDS_INVALID_SHORTCUT_ITEM1, pszString1, hwndMsgOwner, phhctrl);
  1182. return FALSE;
  1183. }
  1184. *pszComma = '\0';
  1185. RemoveTrailingSpaces(csz.psz);
  1186. PCSTR pszClass = csz.psz;
  1187. PSTR pszApplication = FirstNonSpace(pszComma + 1);
  1188. pszComma = StrChr(pszApplication, ',');
  1189. if (pszComma)
  1190. *pszComma = '\0';
  1191. RemoveTrailingSpaces(pszApplication);
  1192. PSTR pszParams = "";
  1193. if (pszComma) {
  1194. pszParams = FirstNonSpace(pszComma + 1);
  1195. RemoveTrailingSpaces(pszParams);
  1196. }
  1197. PSTR pszUrl;
  1198. UINT msg;
  1199. WPARAM wParam;
  1200. LPARAM lParam;
  1201. if (!IsEmptyString(pszString2)) {
  1202. CStr cszArg;
  1203. pszUrl = cszArg.GetArg(pszString2, TRUE);
  1204. msg = Atoi(cszArg.psz);
  1205. pszUrl = cszArg.GetArg(pszUrl, TRUE);
  1206. wParam = Atoi(cszArg.psz);
  1207. pszUrl = cszArg.GetArg(pszUrl, TRUE);
  1208. lParam = Atoi(cszArg.psz);
  1209. pszUrl = FirstNonSpace(pszUrl);
  1210. }
  1211. else {
  1212. pszUrl = (PSTR) txtZeroLength;
  1213. msg = 0;
  1214. }
  1215. CStr strUrl;
  1216. ASSERT(phhctrl->m_pWebBrowserApp != NULL);
  1217. phhctrl->m_pWebBrowserApp->GetLocationURL(&strUrl);
  1218. // check for NULL pointer
  1219. //
  1220. if(strUrl.IsEmpty())
  1221. goto Fail;
  1222. // Execute the shortcut only if we're in a local CHM file.
  1223. //
  1224. if(strstr(strUrl, "\\\\") || strstr(strUrl, "//"))
  1225. goto Fail;
  1226. if (IsEmptyString(pszClass) || !(hwndApp = FindWindow(pszClass, NULL))) {
  1227. // 27-Sep-1997 [ralphw] We special-case shell32.dll,SHHelpShortcuts_RunDLL"
  1228. // in order to run the shortcut without having to load rundll32.
  1229. if (IsSamePrefix(pszParams, txtShellShortCut)) {
  1230. if (!pSHHelpShortcuts_RunDLL) {
  1231. HINSTANCE hmod = LoadLibrary(txtShell32Dll);
  1232. if (hmod) {
  1233. (FARPROC&) pSHHelpShortcuts_RunDLL = GetProcAddress(hmod,
  1234. "SHHelpShortcuts_RunDLL");
  1235. }
  1236. }
  1237. if (pSHHelpShortcuts_RunDLL) {
  1238. pSHHelpShortcuts_RunDLL(NULL, _Module.GetModuleInstance(),
  1239. FirstNonSpace(pszParams + sizeof(txtShellShortCut)),
  1240. SW_SHOW);
  1241. return TRUE;
  1242. }
  1243. }
  1244. hinstRet = ShellExecute(hwndMsgOwner,
  1245. ((strstr(pszApplication, ".cpl") || strstr(pszApplication, ".CPL")) ? txtCplOpen : txtOpen),
  1246. pszApplication, pszParams, "", SW_SHOW);
  1247. if ( hinstRet != (HANDLE)-1 && hinstRet <= (HANDLE)32) {
  1248. AuthorMsg(IDS_CANNOT_RUN, pszApplication, hwndMsgOwner, phhctrl);
  1249. Fail:
  1250. if (phhctrl->m_pszWindow) {
  1251. if (IsCompiledHtmlFile(phhctrl->m_pszWindow, NULL))
  1252. OnDisplayTopic(hwndMsgOwner, phhctrl->m_pszWindow, 0);
  1253. else {
  1254. CWStr cwJump(phhctrl->m_pszWindow);
  1255. HlinkSimpleNavigateToString(cwJump, NULL,
  1256. NULL, phhctrl->GetIUnknown(), NULL, NULL, 0, NULL);
  1257. }
  1258. }
  1259. return FALSE;
  1260. }
  1261. }
  1262. else {
  1263. if (IsIconic(hwndApp))
  1264. ShowWindow(hwndApp, SW_RESTORE); // Must restore minimized app
  1265. SetForegroundWindow(hwndApp);
  1266. }
  1267. if (msg > 0) {
  1268. int i;
  1269. if (!hwndApp) {
  1270. // Wait for up to 7 seconds for the process to initialize
  1271. for (i = 0; i < 70; i++) {
  1272. if ((hwndApp = FindWindow(pszClass, NULL)))
  1273. break;
  1274. Sleep(100);
  1275. }
  1276. }
  1277. if (!hwndApp) {
  1278. // Probably means the window class has changed.
  1279. AuthorMsg(IDS_CLASS_NOT_FOUND, pszClass, hwndMsgOwner, phhctrl);
  1280. if (phhctrl->m_pszWindow) {
  1281. if (IsCompiledHtmlFile(phhctrl->m_pszWindow, NULL))
  1282. OnDisplayTopic(hwndMsgOwner, phhctrl->m_pszWindow, 0);
  1283. else {
  1284. CWStr cwJump(phhctrl->m_pszWindow);
  1285. HlinkSimpleNavigateToString(cwJump, NULL,
  1286. NULL, phhctrl->GetIUnknown(), NULL, NULL, 0, NULL);
  1287. }
  1288. }
  1289. return FALSE;
  1290. }
  1291. SetForegroundWindow(hwndApp);
  1292. if (msg)
  1293. PostMessage(hwndApp, msg, wParam, lParam);
  1294. }
  1295. return TRUE;
  1296. }
  1297. BOOL CAboutBox::OnBeginOrEnd(void)
  1298. {
  1299. if (m_fInitializing) {
  1300. if (m_phhCtrl && m_phhCtrl->m_ptblItems && m_phhCtrl->m_ptblItems->CountStrings())
  1301. SetWindowText(m_phhCtrl->m_ptblItems->GetPointer(1));
  1302. for (int id = IDC_LINE1; id <= IDC_LINE3; id++) {
  1303. // -2 because CTable is 1-based, and we skip over the title
  1304. if (m_phhCtrl->m_ptblItems == NULL)
  1305. break;
  1306. if (id - IDC_LINE1 > m_phhCtrl->m_ptblItems->CountStrings() - 2)
  1307. break;
  1308. SetWindowText(id,
  1309. m_phhCtrl->m_ptblItems->GetPointer((id - IDC_LINE1) + 2));
  1310. }
  1311. // Hide any unused controls
  1312. while (id <= IDC_LINE3)
  1313. HideWindow(id++);
  1314. }
  1315. return TRUE;
  1316. }
  1317. void CHtmlHelpControl::OnDrawStaticText(DRAWITEMSTRUCT* pdis)
  1318. {
  1319. if (!m_pwszButtonText || !*m_pwszButtonText )
  1320. return;
  1321. // REVIEW: since we are the only ones drawing into this DC, do we really
  1322. // need to restore the previous background mode and foreground text color?
  1323. int iBack = SetBkMode(pdis->hDC, TRANSPARENT);
  1324. COLORREF clrLast = CLR_INVALID;
  1325. if (m_clrFont != CLR_INVALID)
  1326. clrLast = SetTextColor(pdis->hDC, m_clrFont);
  1327. RECT rc;
  1328. GetClientRect(pdis->hwndItem, &rc);
  1329. IntlExtTextOutW(pdis->hDC, rc.left, rc.top, ETO_RTLREADING, &rc, m_pwszButtonText, lstrlenW(m_pwszButtonText), NULL);
  1330. // DrawTextEx(pdis->hDC, (PSTR) m_pszButtonText, -1, &rc, DT_BOTTOM | DT_LEFT | DT_NOCLIP | DT_SINGLELINE | DT_NOPREFIX | DT_RTLREADING, NULL);
  1331. if ( pdis->hwndItem == ::GetFocus() )
  1332. ::DrawFocusRect(pdis->hDC, &rc);
  1333. SetBkMode(pdis->hDC, iBack);
  1334. if (clrLast != CLR_INVALID)
  1335. SetTextColor(pdis->hDC, clrLast);
  1336. }
  1337. void CHtmlHelpControl::OnRelatedMenu()
  1338. {
  1339. POINT pt;
  1340. GetCursorPos(&pt);
  1341. #if 1 // reverted bug fix #5516
  1342. HWND hwnd=GetFocus();
  1343. if ( hwnd )
  1344. {
  1345. DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE );
  1346. if ( dwStyle & BS_NOTIFY )
  1347. {
  1348. RECT rc;
  1349. if( GetWindowRect(hwnd, &rc) ) {
  1350. pt.y = rc.top+((rc.bottom-rc.top)/2);
  1351. pt.x = rc.left + ((RECT_HEIGHT(rc)/3)*2); // two-thirds of the height of the button
  1352. }
  1353. }
  1354. }
  1355. #endif
  1356. HMENU hMenu = CreatePopupMenu();
  1357. if (!hMenu)
  1358. return; // BUGBUG: nag the help author
  1359. for (int i = 1; i <= m_pSiteMap->Count(); i++) {
  1360. HxAppendMenu(hMenu, MF_STRING, i,
  1361. m_pSiteMap->GetSiteMapEntry(i)->pszText);
  1362. }
  1363. int iIndex = TrackPopupMenu(hMenu,
  1364. TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD,
  1365. pt.x, pt.y, 0, (m_hwnd == NULL ? hwnd : m_hwnd), NULL);
  1366. if (iIndex)
  1367. JumpToUrl(m_pSiteMap->GetSiteMapEntry(iIndex), m_pSiteMap);
  1368. DestroyMenu(hMenu);
  1369. }
  1370. void CHtmlHelpControl::OnRelatedCommand(int idCommand)
  1371. {
  1372. if (idCommand <= IDM_RELATED_TOPIC)
  1373. return;
  1374. if (m_action != ACT_RELATED_TOPICS) {
  1375. if (!m_ptblTitles)
  1376. return;
  1377. char szURL[INTERNET_MAX_URL_LENGTH];
  1378. int iIndex = m_ptblTitles->GetInt(idCommand - IDM_RELATED_TOPIC);
  1379. m_ptblURLs->GetString( szURL, iIndex );
  1380. if (m_pszWindow) {
  1381. CStr csz(">");
  1382. csz += m_pszWindow;
  1383. OnDisplayTopic(m_hwnd, csz, (DWORD_PTR) szURL);
  1384. return;
  1385. }
  1386. CWStr cwJump(szURL);
  1387. CWStr cwFrame(m_pszFrame ? m_pszFrame : txtZeroLength);
  1388. HlinkSimpleNavigateToString(cwJump, NULL,
  1389. cwFrame, GetIUnknown(), NULL, NULL, 0, NULL);
  1390. delete m_ptblTitles;
  1391. m_ptblTitles = NULL;
  1392. delete m_ptblURLs;
  1393. m_ptblURLs = NULL;
  1394. delete m_ptblLocations;
  1395. m_ptblLocations = NULL;
  1396. return;
  1397. }
  1398. if (idCommand >= ID_VIEW_ENTRY) {
  1399. DisplayAuthorInfo(m_pSiteMap,
  1400. m_pSiteMap->GetSiteMapEntry(idCommand - ID_VIEW_ENTRY));
  1401. return;
  1402. }
  1403. #ifdef _DEBUG
  1404. int pos = (idCommand - IDM_RELATED_TOPIC);
  1405. SITEMAP_ENTRY* pSiteMapEntry = m_pSiteMap->GetSiteMapEntry(pos);
  1406. #endif
  1407. JumpToUrl(m_pSiteMap->GetSiteMapEntry(idCommand - IDM_RELATED_TOPIC),
  1408. m_pSiteMap);
  1409. }
  1410. void CHtmlHelpControl::OnKeywordSearch(int idCommand)
  1411. {
  1412. CSiteMap* pWebMap;
  1413. if (IsEmptyString(m_pszWebMap)) {
  1414. // BUGBUG: nag the author
  1415. return;
  1416. }
  1417. else { // use brace to enclose CHourGlass
  1418. // REVIEW: should we capture the mouse?
  1419. CHourGlass hourglass;
  1420. TCHAR szPath[MAX_PATH];
  1421. if (!ConvertToCacheFile(m_pszWebMap, szPath)) {
  1422. CStr cszMsg(IDS_CANT_FIND_FILE, m_pszWebMap);
  1423. MsgBox(cszMsg);
  1424. return;
  1425. }
  1426. if (m_pindex && isSameString(szPath, m_pindex->GetSiteMapFile()))
  1427. pWebMap = m_pindex;
  1428. else {
  1429. UINT CodePage = 0;
  1430. if( m_pindex && m_pindex->m_phh && m_pindex->m_phh->m_phmData ) {
  1431. CodePage = m_pindex->m_phh->m_phmData->GetInfo()->GetCodePage();
  1432. }
  1433. if (!m_pSiteMap->ReadFromFile(szPath, TRUE, this, CodePage))
  1434. return; // we assume author has already been notified
  1435. pWebMap = m_pSiteMap;
  1436. }
  1437. }
  1438. int end = m_ptblItems->CountStrings();
  1439. int endWebMap = pWebMap->CountStrings();
  1440. UINT CodePage = m_pSiteMap->GetCodePage();
  1441. CWTable tblTitles( CodePage );
  1442. for (int pos = 1; pos <= end; pos++) {
  1443. PCSTR pszKeyword = m_ptblItems->GetPointer(pos);
  1444. for (int posSite = 1; posSite <= endWebMap; posSite++) {
  1445. SITEMAP_ENTRY* pWebMapEntry = pWebMap->GetSiteMapEntry(posSite);
  1446. if (lstrcmpi(pszKeyword, pWebMapEntry->GetKeyword()) == 0) {
  1447. SITE_ENTRY_URL* pUrl = (SITE_ENTRY_URL*) pWebMapEntry->pUrls;
  1448. for (int url = 0; url < pWebMapEntry->cUrls; url++) {
  1449. tblTitles.AddString(posSite, pWebMapEntry->GetTitle(pUrl));
  1450. pUrl = pWebMap->NextUrlEntry(pUrl);
  1451. }
  1452. }
  1453. }
  1454. }
  1455. // we can sort the title table since it contains the index value
  1456. // of the associated URL so just make sure to always fetch the
  1457. // URL index from the selected title string and use that to get the URL
  1458. if( /*bAlphaSortHits*/ TRUE ) {
  1459. tblTitles.SetSorting(GetSystemDefaultLCID());
  1460. tblTitles.SortTable(sizeof(HASH));
  1461. }
  1462. CTopicList TopicList(this, &tblTitles, m_hfont);
  1463. if (TopicList.DoModal())
  1464. {
  1465. PCSTR pszTitle = tblTitles.GetHashStringPointer(TopicList.m_pos);
  1466. SITEMAP_ENTRY* pWebMapEntry = pWebMap->GetSiteMapEntry(
  1467. tblTitles.GetInt(TopicList.m_pos));
  1468. // Now find the actual URL that matches the title
  1469. // BUGBUG: fails with duplicate titles
  1470. for (int url = 0; url < pWebMapEntry->cUrls; url++) {
  1471. if (strcmp(pszTitle, pWebMap->GetUrlTitle(pWebMapEntry, url)) == 0)
  1472. break;
  1473. }
  1474. ASSERT(url < pWebMapEntry->cUrls);
  1475. JumpToUrl(pWebMapEntry, pWebMap, pWebMap->GetUrlEntry(pWebMapEntry, url));
  1476. }
  1477. }
  1478. // for bug #3681 -- don't use this new function for any other reason under any circumstances.
  1479. HWND OnDisplayTopicWithRMS(HWND hwndCaller, LPCSTR pszFile, DWORD_PTR dwData)
  1480. {
  1481. BOOL bCollection = IsCollectionFile(pszFile);
  1482. CExTitle* pTitle = NULL;
  1483. CExCollection* pCollection = NULL;
  1484. BOOL bCompiled;
  1485. if( bCollection )
  1486. bCompiled = IsCompiledURL( (PCSTR) dwData );
  1487. else
  1488. bCompiled = IsCompiledURL( pszFile );
  1489. if( bCompiled ) {
  1490. CExCollection* pCollection = GetCurrentCollection(NULL, pszFile);
  1491. if( pCollection )
  1492. if( (PCSTR) dwData )
  1493. HRESULT hr = pCollection->URL2ExTitle( (PCSTR) dwData, &pTitle );
  1494. }
  1495. if( pCollection && bCompiled && (!pTitle || FAILED( EnsureStorageAvailability( pTitle, HHRMS_TYPE_TITLE, TRUE, TRUE, FALSE )) ) ) {
  1496. g_LastError.Set(HH_E_FILENOTFOUND) ;
  1497. return NULL;
  1498. }
  1499. return OnDisplayTopic(hwndCaller, pszFile, dwData );
  1500. }
  1501. ///////////////////////////////////////////////////////////
  1502. //
  1503. // OnKeywordSearch - Handles HH_KEYWORD_LOOKUP Command
  1504. //
  1505. HWND OnKeywordSearch(HWND hwndCaller, PCSTR pszFile, HH_AKLINK* pakLink, BOOL fKLink)
  1506. {
  1507. CStr cszKeywords; // Use so StrToken can modify them. Declared here so JumpNotFound can use.
  1508. CStr cszCompressed;
  1509. BOOL bCollection = IsCollectionFile(pszFile);
  1510. // We need the following in a bunch of locations in the code below. However, I have no
  1511. // idea which of the following functions may cause side affects which affect this line.
  1512. // Therefore, I can't with any assurance improve the performace of this code.
  1513. // CHHWinType* phh = FindCurProccesWindow(idProcess);
  1514. if (bCollection || IsCompiledHtmlFile(pszFile, &cszCompressed))
  1515. {
  1516. if (bCollection)
  1517. cszCompressed = pszFile;
  1518. CHmData* phmData = FindCurFileData(cszCompressed);
  1519. if (!phmData)
  1520. goto JumpNotFound;
  1521. UINT CodePage = phmData->m_pTitleCollection->GetMasterTitle()->GetInfo()->GetCodePage();
  1522. CTable tblItems;
  1523. CWTable tblTitles( CodePage );
  1524. CWTable tblLocations( CodePage );
  1525. CTable tblURLs;
  1526. if (IsEmptyString(pakLink->pszKeywords))
  1527. goto JumpNotFound;
  1528. if (pakLink->fReserved)
  1529. cszKeywords = (WCHAR*) pakLink->pszKeywords;
  1530. else
  1531. cszKeywords = pakLink->pszKeywords;
  1532. tblItems.AddString( "" ); // set item1 to NULL -- no external titles
  1533. tblItems.AddString( cszKeywords );
  1534. GetWordWheelHits( phmData->m_pTitleCollection, &tblItems,
  1535. &tblURLs, &tblTitles, &tblLocations,
  1536. fKLink, FALSE, FALSE );
  1537. if (tblURLs.CountStrings() < 1)
  1538. {
  1539. // No links found.
  1540. goto JumpNotFound ;
  1541. }
  1542. // if only one topic then jump to it
  1543. if( tblURLs.CountStrings() == 1 )
  1544. {
  1545. char szURL[INTERNET_MAX_URL_LENGTH];
  1546. tblURLs.GetString( szURL, 1 );
  1547. //TODO: This code is repeated below. Share.
  1548. if (pakLink->pszWindow)
  1549. {
  1550. strcat(szURL, ">");
  1551. strcat(szURL, (*pakLink->pszWindow == '>' ?
  1552. pakLink->pszWindow + 1 : pakLink->pszWindow));
  1553. }
  1554. else if (bCollection && phmData->GetDefaultWindow()) // Use the default window for the collection. HH 3428
  1555. {
  1556. strcat(szURL, ">");
  1557. strcat(szURL, (*phmData->GetDefaultWindow() == '>' ?
  1558. phmData->GetDefaultWindow() + 1 : phmData->GetDefaultWindow()));
  1559. }
  1560. else //REVIEW: Can we find other possible default windows?
  1561. {
  1562. // Pick a random window type. Its unlikely that this is the one you really want, since you get the first one for this process.
  1563. CHHWinType* phh = FindCurWindow();
  1564. if (phh)
  1565. {
  1566. strcat(szURL, ">");
  1567. strcat(szURL, phh->pszType);
  1568. }
  1569. }
  1570. if( bCollection )
  1571. return OnDisplayTopicWithRMS(hwndCaller, pszFile, (DWORD_PTR) szURL);
  1572. return OnDisplayTopicWithRMS(hwndCaller, szURL, 0);
  1573. }
  1574. // Determine the dialogs parent.
  1575. HWND hwndDlgParent = hwndCaller ;
  1576. CHHWinType* phh = FindCurWindow();
  1577. if (phh)
  1578. {
  1579. // If there is an existing help window, use it for the dialog parent instead of
  1580. // the hwnd passed from the caller. The reason is that the disambiguator will not be
  1581. // modal with the help window, but with the calling app.
  1582. HWND hwnd = phh->GetHwnd();
  1583. if (hwnd && ::IsValidWindow(hwnd))
  1584. {
  1585. hwndDlgParent = hwnd ;
  1586. if (hwnd != GetForegroundWindow())
  1587. {
  1588. BOOL b = SetForegroundWindow(hwnd) ;
  1589. ASSERT(b) ;
  1590. }
  1591. }
  1592. }
  1593. // we can sort the title table since it contains the index value
  1594. // of the associated URL so just make sure to always fetch the
  1595. // URL index from the selected title string and use that to get the URL
  1596. if( /*bAlphaSortHits*/ TRUE ) {
  1597. tblTitles.SetSorting(GetSystemDefaultLCID());
  1598. tblTitles.SortTable(sizeof(HASH));
  1599. }
  1600. // Display a dialog containing the links to the user.
  1601. CTopicList TopicList(hwndDlgParent, &tblTitles, phmData->GetContentFont(), &tblLocations);
  1602. if (TopicList.DoModal())
  1603. {
  1604. char szURL[INTERNET_MAX_URL_LENGTH];
  1605. int iIndex = tblTitles.GetInt(TopicList.m_pos);
  1606. tblURLs.GetString( szURL, iIndex );
  1607. //TODO: This code is repeated above. Share.
  1608. if (pakLink->pszWindow)
  1609. {
  1610. strcat(szURL, ">");
  1611. strcat(szURL, (*pakLink->pszWindow == '>' ?
  1612. pakLink->pszWindow + 1 : pakLink->pszWindow));
  1613. }
  1614. else if (bCollection && phmData->GetDefaultWindow()) // Use the default window for the collection. HH 3428
  1615. {
  1616. strcat(szURL, ">");
  1617. strcat(szURL, (*phmData->GetDefaultWindow() == '>' ?
  1618. phmData->GetDefaultWindow() + 1 : phmData->GetDefaultWindow()));
  1619. }
  1620. else
  1621. {
  1622. // Pick a random window type. Its unlikely that this is the one you really want, since you get the first one for this process.
  1623. CHHWinType* phh = FindCurWindow();
  1624. if (phh)
  1625. {
  1626. strcat(szURL, ">");
  1627. strcat(szURL, phh->pszType);
  1628. }
  1629. }
  1630. if( bCollection )
  1631. return OnDisplayTopicWithRMS(hwndCaller, pszFile, (DWORD_PTR) szURL);
  1632. return OnDisplayTopicWithRMS(hwndCaller, szURL, 0);
  1633. }
  1634. else //REVIEW: Can we find other possible default windows?
  1635. {
  1636. // Signal that we succeeded, but didn't do anything.
  1637. g_LastError.Set(S_FALSE) ;
  1638. // User canceled dialog box. Don't go to JumpNotFound.
  1639. return NULL ;
  1640. }
  1641. }
  1642. // Error handling.
  1643. JumpNotFound:
  1644. if (pakLink->fIndexOnFail) // Display index if the keyword is not found.
  1645. {
  1646. // We only seed the edit control with the characters to the ';'.
  1647. // The StrToken command happens to repalce the ';' with a \0.
  1648. // So we can use cszKeywords, directly as the first keyword.
  1649. // Also, note that cszKeywords.psz is set to NULL during initialization.
  1650. return doDisplayIndex(hwndCaller, pszFile, cszKeywords);
  1651. }
  1652. else if (pakLink->pszUrl) // Display the page pointered to by pszUrl when keyword is not found.
  1653. {
  1654. if (pakLink->pszWindow)
  1655. {
  1656. cszCompressed += ">";
  1657. cszCompressed += (*pakLink->pszWindow == '>' ?
  1658. pakLink->pszWindow + 1 : pakLink->pszWindow);
  1659. }
  1660. else
  1661. {
  1662. CHHWinType* phh = FindCurWindow();
  1663. if (phh)
  1664. {
  1665. cszCompressed += ">";
  1666. cszCompressed += phh->pszType;
  1667. }
  1668. }
  1669. return OnDisplayTopicWithRMS(hwndCaller, cszCompressed, (DWORD_PTR) pakLink->pszUrl);
  1670. }
  1671. else if (pakLink->pszMsgText) // Display a message box with the callers messages.
  1672. {
  1673. MessageBox(hwndCaller, pakLink->pszMsgText,
  1674. IsEmptyString(pakLink->pszMsgTitle) ? "" : pakLink->pszMsgTitle,
  1675. MB_OK | MB_ICONEXCLAMATION);
  1676. }
  1677. return NULL;
  1678. }
  1679. ///////////////////////////////////////////////////////////
  1680. //
  1681. // OnSample - Handles HH_COPY_SAMPLE Command
  1682. //
  1683. BOOL CHtmlHelpControl::OnCopySample()
  1684. {
  1685. if (!m_ptblItems)
  1686. return FALSE;
  1687. // Get the SFL name from "item2"
  1688. //
  1689. CStr cszSFLFileName;
  1690. if (!IsEmptyString(m_ptblItems->GetPointer(2)))
  1691. cszSFLFileName = m_ptblItems->GetPointer(2);
  1692. else
  1693. return FALSE;
  1694. // Compute the SFL base name
  1695. //
  1696. char szSFLBaseName[_MAX_FNAME];
  1697. strncpy(szSFLBaseName,cszSFLFileName, _MAX_FNAME-1);
  1698. szSFLBaseName[_MAX_FNAME-1] = NULL;
  1699. cszSFLFileName = szSFLBaseName;
  1700. cszSFLFileName+=".SFL";
  1701. // Get the dialog title from "item1"
  1702. //
  1703. CStr cszDialogTitle;
  1704. if (!IsEmptyString(m_ptblItems->GetPointer(1)))
  1705. cszDialogTitle = m_ptblItems->GetPointer(1);
  1706. else
  1707. cszDialogTitle = "Sample Application";
  1708. // Get the current URL
  1709. //
  1710. CStr cszCurUrl;
  1711. if (m_pWebBrowserApp != NULL)
  1712. m_pWebBrowserApp->GetLocationURL(&cszCurUrl);
  1713. else
  1714. return FALSE;
  1715. char szSflRelativeUrl[INTERNET_MAX_URL_LENGTH];
  1716. char szSflUrl[INTERNET_MAX_URL_LENGTH];
  1717. DWORD dwLength = sizeof(szSflUrl);
  1718. // Create URL to SFL file
  1719. //
  1720. wsprintf(szSflRelativeUrl,"/samples/%s/%s",szSFLBaseName,cszSFLFileName);
  1721. strcpy(szSflUrl,cszCurUrl);
  1722. char *pszTemp = szSflUrl;
  1723. // Locate the :: in the URL
  1724. //
  1725. while(*pszTemp && !(*pszTemp == ':' && *(pszTemp+1) == ':'))
  1726. ++pszTemp;
  1727. // return if no :: was found
  1728. //
  1729. if(!*pszTemp)
  1730. return FALSE;
  1731. pszTemp+=2;
  1732. // place a null after ::
  1733. //
  1734. *pszTemp = 0;
  1735. strcat(szSflUrl,szSflRelativeUrl);
  1736. // download the SFL file
  1737. //
  1738. char szPath[MAX_PATH],szSFLFilePath[MAX_PATH];
  1739. GetTempPath(sizeof(szPath),szPath);
  1740. GetTempFileName(szPath,"SFL",0, szSFLFilePath);
  1741. HRESULT hr = URLDownloadToFile(NULL, szSflUrl, szSFLFilePath, 0, NULL);
  1742. if (FAILED(hr))
  1743. DeleteFile(szSFLFilePath);
  1744. if(!FAILED(hr))
  1745. {
  1746. // Process compressed sample
  1747. //
  1748. // Create URL to SFL file
  1749. //
  1750. // Null after the :: again in szSflUrl
  1751. //
  1752. *pszTemp = 0;
  1753. char szSampleBaseUrl[INTERNET_MAX_URL_LENGTH];
  1754. dwLength = sizeof(szSampleBaseUrl);
  1755. wsprintf(szSampleBaseUrl,"%s/samples/%s/",szSflUrl,szSFLBaseName);
  1756. // call sample UI
  1757. //
  1758. return ProcessSample(szSFLFilePath, szSampleBaseUrl, cszDialogTitle, this, TRUE);
  1759. // delete the copy of the SFL file
  1760. DeleteFile(szSFLFilePath);
  1761. }
  1762. else
  1763. {
  1764. CStr cstr;
  1765. PSTR psz = NULL;
  1766. if ( m_pWebBrowserApp )
  1767. {
  1768. m_pWebBrowserApp->GetLocationURL(&cstr);
  1769. psz = (PSTR)cstr;
  1770. }
  1771. // Process uncompressed sample
  1772. CExCollection *pCurCollection = GetCurrentCollection(NULL, (LPCSTR)psz);
  1773. if(!pCurCollection)
  1774. return FALSE;
  1775. CExTitle *pExTitle = NULL;
  1776. // Get the CExTitle object associated to this URL
  1777. //
  1778. pCurCollection->URL2ExTitle(cszCurUrl, &pExTitle);
  1779. if(!pExTitle)
  1780. return FALSE;
  1781. // Make sure the title is open
  1782. // BUGBUG: what do we do if this fails?
  1783. pExTitle->Init();
  1784. TCHAR *pszSampleLocation = NULL;
  1785. // Make sure the CTitle object is initialized
  1786. //
  1787. if(pExTitle->m_pTitle)
  1788. {
  1789. // Get the sample location from the CTitle object
  1790. //
  1791. if (pExTitle->GetUsedLocation())
  1792. pszSampleLocation = pExTitle->GetUsedLocation()->SampleLocation;
  1793. }
  1794. if(!pszSampleLocation)
  1795. {
  1796. // get sample location from CCollection if get from CTitle failed
  1797. pszSampleLocation = pCurCollection->m_Collection.GetSampleLocation();
  1798. }
  1799. // Make sure we got a sample location
  1800. //
  1801. if(!pszSampleLocation)
  1802. return FALSE;
  1803. CLocation *pLocation = pCurCollection->m_Collection.FindLocation(pszSampleLocation);
  1804. if(!pLocation)
  1805. return FALSE;
  1806. char szSampleBasePath[MAX_PATH],szSFLFilePath[MAX_PATH];
  1807. // construct full path to uncompressed sample directory
  1808. //
  1809. strcpy(szSampleBasePath,pLocation->GetPath());
  1810. // CatPath(szSampleBasePath,szSFLBaseName);
  1811. // strcat(szSampleBasePath,"\\");
  1812. // construct full path to SFL
  1813. //
  1814. strcpy(szSFLFilePath,szSampleBasePath);
  1815. CatPath(szSFLFilePath,"\\SFL\\");
  1816. CatPath(szSFLFilePath,cszSFLFileName);
  1817. // make sure the sample (CD) is available
  1818. if( pExTitle ) {
  1819. pExTitle->SetCurrentAttachmentName( szSFLFilePath );
  1820. if( FAILED(hr = EnsureStorageAvailability( pExTitle, HHRMS_TYPE_ATTACHMENT ) ) ) {
  1821. if( hr == HHRMS_E_SKIP || hr == HHRMS_E_SKIP_ALWAYS )
  1822. return TRUE;
  1823. return FALSE;
  1824. }
  1825. }
  1826. // if the user updated the CD location then we need to update the SFL file location
  1827. // before we process the sample
  1828. if( hr == HHRMS_S_LOCATION_UPDATE ) {
  1829. strcpy(szSampleBasePath,pLocation->GetPath());
  1830. strcpy(szSFLFilePath,szSampleBasePath);
  1831. CatPath(szSFLFilePath,"\\SFL\\");
  1832. CatPath(szSFLFilePath,cszSFLFileName);
  1833. }
  1834. // call sample UI
  1835. //
  1836. return ProcessSample(szSFLFilePath, szSampleBasePath, cszDialogTitle, this, FALSE);
  1837. }
  1838. return TRUE;
  1839. }