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.

556 lines
16 KiB

  1. // Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved.
  2. #include "header.h"
  3. #include "fts.h"
  4. //#include <commctrl.h>
  5. #include "cctlww.h"
  6. #include "hhtypes.h"
  7. #include "parserhh.h"
  8. #include "collect.h"
  9. #include "toc.h"
  10. #include "system.h"
  11. #include "listview.h"
  12. #include "secwin.h" // for DEFAULT_NAV_WIDTH;
  13. ///////////////////////////////////////////////////////////
  14. //
  15. // Constants
  16. //
  17. const int c_TopicColumn = 0;
  18. const int c_LocationColumn = 1;
  19. const int c_RankColumn = 2;
  20. ///////////////////////////////////////////////////////////
  21. //
  22. // ListViewCompareProc - Used to sort columns in Advanced Search UI mode.
  23. //
  24. int CALLBACK ListViewCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
  25. ///////////////////////////////////////////////////////////
  26. //
  27. //
  28. //
  29. typedef struct tag_RESULTSSORTINFO
  30. {
  31. CFTSListView* pThis; // The ListView object controlling the sort.
  32. int iSubItem; // column we are sorting.
  33. LCID lcid; // locale to sort by
  34. WCHAR* pwszUntitled;
  35. WCHAR* pwszUnknown;
  36. } RESULTSSORTINFO;
  37. ///////////////////////////////////////////////////////////
  38. //
  39. // Constructor
  40. //
  41. CFTSListView::CFTSListView(CExCollection* pTitleCollection, HWND hwndListView, bool bAdvancedSearch)
  42. {
  43. m_bSizeColumnsInit = false;
  44. m_pTitleCollection = pTitleCollection;
  45. m_bAdvancedSearch = bAdvancedSearch;
  46. if ( hwndListView != NULL )
  47. {
  48. m_hwndListView = hwndListView;
  49. m_fInitialized = TRUE;
  50. W_EnableUnicode(hwndListView, W_ListView);
  51. //---Set the column headings
  52. if (bAdvancedSearch)
  53. {
  54. // Add the columns
  55. LV_COLUMNW column;
  56. column.mask = LVCF_FMT | LVCF_TEXT;
  57. if(g_fBiDi)
  58. column.fmt = LVCFMT_RIGHT;
  59. else
  60. column.fmt = LVCFMT_LEFT;
  61. // Title Column
  62. column.pszText = (PWSTR)GetStringResourceW(IDS_ADVSEARCH_HEADING_TITLE);
  63. int iCol = c_TopicColumn;
  64. int iResult = W_ListView_InsertColumn(hwndListView, iCol++, &column);
  65. ASSERT(iResult != -1);
  66. // Bidi - this is necessary to get the LVCFMT_RIGHT formatting to stick
  67. if(g_fBiDi)
  68. {
  69. column.mask = LVCF_FMT;
  70. column.fmt = LVCFMT_RIGHT;
  71. SendMessage(hwndListView,LVM_SETCOLUMN, (WPARAM) 0, (LPARAM) &column);
  72. }
  73. column.mask = LVCF_FMT | LVCF_TEXT;
  74. // Location column
  75. column.pszText = (PWSTR)GetStringResourceW(IDS_ADVSEARCH_HEADING_LOCATION);
  76. iResult = W_ListView_InsertColumn(hwndListView, iCol++, &column);
  77. ASSERT(iResult != -1);
  78. // Rank Column.
  79. column.pszText = (PWSTR)GetStringResourceW(IDS_ADVSEARCH_HEADING_RANK);
  80. iResult = W_ListView_InsertColumn(hwndListView, iCol++, &column);
  81. ASSERT(iResult != -1);
  82. // We want most of the space to be taken up by first column.
  83. // Set the columns up for autosizing.
  84. W_ListView_SetColumnWidth(hwndListView, c_RankColumn, LVSCW_AUTOSIZE);
  85. W_ListView_SetColumnWidth(hwndListView, c_LocationColumn, LVSCW_AUTOSIZE);
  86. W_ListView_SetColumnWidth(hwndListView, c_TopicColumn, LVSCW_AUTOSIZE_USEHEADER);
  87. // Get the default width of the client. Assumes that the dialog is the default size at startup.
  88. RECT client;
  89. GetClientRect(hwndListView, &client);
  90. m_cxDefault = client.right - client.left;
  91. }
  92. else
  93. {
  94. RECT rcTemp;
  95. UINT TopicWidth;
  96. if (GetWindowRect(m_hwndListView, &rcTemp))
  97. TopicWidth = rcTemp.right - rcTemp.left;
  98. else
  99. TopicWidth = 122;
  100. LV_COLUMNW column;
  101. column.mask = LVCF_FMT | LVCF_WIDTH;
  102. column.cx = TopicWidth-5;
  103. if(g_fBiDi)
  104. column.fmt = LVCFMT_RIGHT;
  105. else
  106. column.fmt = LVCFMT_LEFT;
  107. W_ListView_InsertColumn( m_hwndListView, 0, &column );
  108. // Bidi - this is necessary to get the LVCFMT_RIGHT formatting to stick
  109. if(g_fBiDi)
  110. {
  111. column.mask = LVCF_FMT;
  112. column.fmt = LVCFMT_RIGHT;
  113. SendMessage(m_hwndListView,LVM_SETCOLUMN, (WPARAM) 0, (LPARAM) &column);
  114. }
  115. }
  116. }
  117. else
  118. m_fInitialized = FALSE;
  119. m_pResults = NULL;
  120. m_ItemNumber = -1;
  121. m_cResultCount = 0;
  122. }
  123. ///////////////////////////////////////////////////////////
  124. //
  125. // SetResults
  126. //
  127. void
  128. CFTSListView::SetResults(int cResultCount, SEARCH_RESULT * SearchResults )
  129. {
  130. ASSERT(cResultCount >0);
  131. ASSERT(SearchResults);
  132. m_cResultCount = cResultCount;
  133. m_pResults = SearchResults;
  134. }
  135. ///////////////////////////////////////////////////////////
  136. //
  137. // AddItem
  138. //
  139. void
  140. CFTSListView::AddItems()
  141. {
  142. ASSERT(m_cResultCount >0);
  143. ASSERT(m_pResults);
  144. LV_ITEMW item; // To add to the list view.
  145. int i;
  146. WCHAR szRank[6];
  147. W_ListView_DeleteAllItems(m_hwndListView);
  148. W_ListView_SetItemCount( m_hwndListView, m_cResultCount );
  149. int slot=0;
  150. int rank = 1;
  151. for ( i=0; i< m_cResultCount; i++)
  152. {
  153. // need to get the topic string from the Topic Number
  154. // Add the Topic string to the List View. Actually using callbacks.
  155. //
  156. item.pszText = LPSTR_TEXTCALLBACKW;
  157. item.mask = LVIF_TEXT|LVIF_PARAM;
  158. item.iImage = 0;
  159. item.state = 0;
  160. item.stateMask = 0;
  161. item.iItem = slot;
  162. item.iSubItem = c_TopicColumn;
  163. item.lParam = i;
  164. W_ListView_InsertItem( m_hwndListView, &item );
  165. //--- Two more columns in Adv FTS mode.
  166. if (m_bAdvancedSearch)
  167. {
  168. //--- Add Location Column
  169. W_ListView_SetItemText(m_hwndListView, slot, c_LocationColumn, LPSTR_TEXTCALLBACKW); // Has an internal item struct.
  170. //--- Add Rank column
  171. //wsprintf(szRank,"%d",rank++);
  172. if(g_langSystem == LANG_ARABIC || g_langSystem == LANG_HEBREW)
  173. {
  174. szRank[0]=0x200E;
  175. _itow(rank++, szRank+1, 10);
  176. }
  177. else
  178. _itow(rank++, szRank, 10);
  179. W_ListView_SetItemText(m_hwndListView, slot, c_RankColumn, szRank); // Has an internal item struct.
  180. }
  181. slot++;
  182. }
  183. // fix for 7024 which was a regression caused by AFTS, we must manually sort rather then using the sort bit
  184. if (!m_bAdvancedSearch)
  185. {
  186. // Get the string for untitled things.
  187. CWStr wstrUntitled(IDS_UNTITLED);
  188. CWStr wstrUnknown(IDS_UNKNOWN);
  189. // Fill this structure to make the sorting quicker/more efficient.
  190. RESULTSSORTINFO Info;
  191. Info.pThis = this;
  192. Info.iSubItem = 0;
  193. Info.lcid = LOCALE_SYSTEM_DEFAULT;
  194. Info.pwszUntitled = wstrUntitled;
  195. Info.pwszUnknown = wstrUnknown;
  196. W_ListView_SortItems(m_hwndListView,
  197. ListViewCompareProc,
  198. reinterpret_cast<LPARAM>(&Info));
  199. }
  200. W_ListView_SetItemState( m_hwndListView, 0, LVIS_SELECTED, LVIF_STATE | LVIS_SELECTED );
  201. }
  202. void CFTSListView::ResetQuery(void)
  203. {
  204. if(m_pResults != NULL )
  205. {
  206. // Free the results list
  207. //
  208. m_pTitleCollection->m_pFullTextSearch->FreeResults( m_pResults );
  209. m_pResults = NULL;
  210. m_cResultCount = 0;
  211. m_ItemNumber = -1;
  212. W_ListView_DeleteAllItems( m_hwndListView );
  213. }
  214. }
  215. ///////////////////////////////////////////////////////////
  216. //
  217. // ListViewMsg - Message notification handler.
  218. //
  219. LRESULT CFTSListView::ListViewMsg(HWND hwnd, NM_LISTVIEW* lParam)
  220. {
  221. DWORD dwTemp;
  222. CExTitle* pTitle;
  223. switch(lParam->hdr.code)
  224. {
  225. case NM_DBLCLK:
  226. case NM_RETURN:
  227. if ( m_ItemNumber == -1 )
  228. break;
  229. dwTemp = m_pResults[m_ItemNumber].dwTopicNumber;
  230. pTitle = m_pResults[m_ItemNumber].pTitle;
  231. if ( pTitle )
  232. {
  233. char szURL[MAX_URL];
  234. if ( (pTitle->GetTopicURL(dwTemp, szURL, sizeof(szURL)) == S_OK) )
  235. ChangeHtmlTopic(szURL, hwnd, 1);
  236. }
  237. break;
  238. case LVN_ITEMCHANGING:
  239. if ( ((NM_LISTVIEW*)lParam)->uNewState & LVIS_SELECTED )
  240. {
  241. // use the item number as an index into the search results array.
  242. // Then use the Topic number to get to the URL to display the Topic.
  243. if( m_pResults != NULL )
  244. {
  245. m_ItemNumber = (int)((NM_LISTVIEW*)lParam)->lParam;
  246. }
  247. }
  248. else
  249. {
  250. // HHBUG 2208 - Need to unmark the item if its not selected.
  251. m_ItemNumber = -1;
  252. }
  253. break;
  254. case LVN_GETDISPINFOA:
  255. OnGetDispInfo((LV_DISPINFO*)lParam);
  256. break;
  257. case LVN_GETDISPINFOW:
  258. OnGetDispInfoW((LV_DISPINFOW*)lParam);
  259. break;
  260. case LVN_COLUMNCLICK:
  261. if (m_bAdvancedSearch)
  262. {
  263. CHourGlass waitcur;
  264. NM_LISTVIEW *pNM = reinterpret_cast<NM_LISTVIEW*>(lParam);
  265. // Get the string for untitled things.
  266. CWStr wstrUntitled(IDS_UNTITLED);
  267. CWStr wstrUnknown(IDS_UNKNOWN);
  268. // Fill this structure to make the sorting quicker/more efficient.
  269. RESULTSSORTINFO Info;
  270. Info.pThis = this;
  271. Info.iSubItem = pNM->iSubItem;
  272. Info.lcid = LOCALE_SYSTEM_DEFAULT;
  273. Info.pwszUntitled = wstrUntitled;
  274. Info.pwszUnknown = wstrUnknown;
  275. W_ListView_SortItems(pNM->hdr.hwndFrom,
  276. ListViewCompareProc,
  277. reinterpret_cast<LPARAM>(&Info));
  278. }
  279. // Fall through...
  280. default:
  281. ;
  282. }
  283. return 0;
  284. }
  285. ///////////////////////////////////////////////////////////
  286. //
  287. // ListViewCompareProc - Used to sort columns in Advanced Search UI mode.
  288. //
  289. int
  290. CALLBACK
  291. ListViewCompareProc( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort )
  292. {
  293. WCHAR wsz1[MAX_TOPIC_NAME];
  294. WCHAR wsz2[MAX_TOPIC_NAME];
  295. int iReturn;
  296. int index1 = (int)lParam1;
  297. int index2 = (int)lParam2;
  298. RESULTSSORTINFO* pInfo = reinterpret_cast<RESULTSSORTINFO*>(lParamSort);
  299. SEARCH_RESULT* pResults = pInfo->pThis->m_pResults;
  300. switch( pInfo->iSubItem )
  301. {
  302. case c_TopicColumn: // Topic String
  303. pResults[index1].pTitle->GetTopicName( pResults[index1].dwTopicNumber, wsz1, MAX_TOPIC_NAME);
  304. if (0 == wsz1[0])
  305. _wcsncpy(wsz1, pInfo->pwszUntitled, MAX_TOPIC_NAME);
  306. pResults[index2].pTitle->GetTopicName( pResults[index2].dwTopicNumber, wsz2, MAX_TOPIC_NAME);
  307. if (0 == wsz2[0])
  308. _wcsncpy(wsz2, pInfo->pwszUntitled, MAX_TOPIC_NAME);
  309. iReturn = W_CompareString(pInfo->lcid, 0, wsz1, -1, wsz2, -1) - 2;
  310. break;
  311. case c_LocationColumn: // Location String
  312. pResults[index1].pTitle->GetTopicLocation( pResults[index1].dwTopicNumber, wsz1, MAX_TOPIC_NAME);
  313. if (0 == wsz1[0])
  314. _wcsncpy(wsz1, pInfo->pwszUnknown, MAX_TOPIC_NAME);
  315. pResults[index2].pTitle->GetTopicLocation( pResults[index2].dwTopicNumber, wsz2, MAX_TOPIC_NAME);
  316. if (0 == wsz2[0])
  317. _wcsncpy(wsz2, pInfo->pwszUnknown, MAX_TOPIC_NAME);
  318. iReturn = W_CompareString(pInfo->lcid, 0, wsz1, -1, wsz2, -1) - 2;
  319. break;
  320. case c_RankColumn: // Rank Number
  321. iReturn = index1 - index2;
  322. break;
  323. default:
  324. ASSERT(0);
  325. iReturn = index1 - index2;
  326. break;
  327. }
  328. return iReturn;
  329. }
  330. ///////////////////////////////////////////////////////////
  331. //
  332. // SizeColumns
  333. //
  334. void
  335. CFTSListView::SizeColumns()
  336. {
  337. //--- Get the size of the client area
  338. RECT rcListView;
  339. ::GetClientRect(m_hwndListView, &rcListView);
  340. int width = (rcListView.right-rcListView.left);
  341. if(!m_bAdvancedSearch)
  342. {
  343. W_ListView_SetColumnWidth(m_hwndListView, 0, width);
  344. return;
  345. }
  346. //--- So some first time initialization
  347. if (!m_bSizeColumnsInit)
  348. {
  349. m_bSizeColumnsInit = true;
  350. // The following little hack should get us the minimun width for the location and rank column.
  351. W_ListView_SetColumnWidth(m_hwndListView, c_TopicColumn, width);
  352. W_ListView_SetColumnWidth(m_hwndListView, c_LocationColumn, LVSCW_AUTOSIZE_USEHEADER);
  353. W_ListView_SetColumnWidth(m_hwndListView, c_RankColumn, LVSCW_AUTOSIZE_USEHEADER);
  354. m_cxLocMin = W_ListView_GetColumnWidth(m_hwndListView, 1);
  355. m_cxRankMin = W_ListView_GetColumnWidth(m_hwndListView, 2);
  356. }
  357. //--- Calculate the column widths
  358. int cxTitle; // Width of the Title Column.
  359. int cxLoc; // Width of the location Column.
  360. int cxRank = m_cxRankMin; // Width of the Rank Column.
  361. if (width >= m_cxDefault)
  362. {
  363. // Everything is fully visible.
  364. cxTitle = width / 2;
  365. cxLoc = (width - cxTitle - cxRank);
  366. }
  367. else if ((width < m_cxDefault) /*&& (width > DEFAULT_NAV_WIDTH/2)*/)
  368. {
  369. // Only part of Rank column is shown and only the min loc size is used.
  370. cxTitle = width / 2;
  371. cxLoc = (width - cxTitle)* 3 /4;
  372. if (cxLoc > m_cxLocMin)
  373. {
  374. // Make sure that we use the min width for the location.
  375. cxTitle += cxLoc - m_cxLocMin; // Add the difference back to the Title column.
  376. cxLoc = m_cxLocMin;
  377. }
  378. }
  379. /*
  380. This branch isn't needed because the pane itself doesn't size below this medium.
  381. else if (width <= DEFAULT_NAV_WIDTH/2)
  382. {
  383. // No Rank is shown. Partial Location is shown.
  384. cxTitle = width * 3/4;
  385. cxLoc = m_cxLocMin;
  386. }
  387. */
  388. W_ListView_SetColumnWidth(m_hwndListView, c_RankColumn, cxRank);
  389. W_ListView_SetColumnWidth(m_hwndListView, c_LocationColumn, cxLoc);
  390. W_ListView_SetColumnWidth(m_hwndListView, c_TopicColumn, cxTitle);
  391. }
  392. ///////////////////////////////////////////////////////////
  393. //
  394. // OnGetDispInfo
  395. //
  396. void
  397. CFTSListView::OnGetDispInfo(LV_DISPINFOA* pDispInfo)
  398. {
  399. static char szTemp[MAX_PATH*4]; // Holds strings.
  400. // Check to see if we have results.
  401. if ( m_pResults != NULL )
  402. {
  403. int i = (int)pDispInfo->item.lParam;
  404. switch(pDispInfo->item.iSubItem)
  405. {
  406. case c_TopicColumn: // Topic
  407. m_pResults[i].pTitle->GetTopicName( m_pResults[i].dwTopicNumber, pDispInfo->item.pszText, pDispInfo->item.cchTextMax );
  408. if (!pDispInfo->item.pszText[0])
  409. {
  410. strncpy(pDispInfo->item.pszText, GetStringResource(IDS_UNTITLED), pDispInfo->item.cchTextMax);
  411. }
  412. // Tell the ListView to store this string.
  413. pDispInfo->item.mask = pDispInfo->item.mask | LVIF_DI_SETITEM;
  414. break;
  415. case c_LocationColumn: // Location
  416. {
  417. ASSERT(m_bAdvancedSearch);
  418. HRESULT hr = m_pResults[i].pTitle->GetTopicLocation(m_pResults[i].dwTopicNumber, szTemp, MAX_PATH*4);
  419. if (FAILED(hr))
  420. {
  421. strcpy(szTemp, GetStringResource(IDS_UNKNOWN));
  422. }
  423. strncpy(pDispInfo->item.pszText, szTemp, pDispInfo->item.cchTextMax);
  424. // Tell the ListView to store this string.
  425. pDispInfo->item.mask = pDispInfo->item.mask | LVIF_DI_SETITEM;
  426. }
  427. break;
  428. #ifdef _DEBUG
  429. case c_RankColumn: // Rank
  430. // shouldn't have a callback. So Fall on down.
  431. default:
  432. ASSERT(0);
  433. break;
  434. #endif
  435. };
  436. }
  437. }
  438. ///////////////////////////////////////////////////////////
  439. //
  440. // OnGetDispInfo
  441. //
  442. void
  443. CFTSListView::OnGetDispInfoW(LV_DISPINFOW* pDispInfo)
  444. {
  445. HRESULT hr;
  446. // Check to see if we have results.
  447. if ( m_pResults != NULL )
  448. {
  449. int i = (int)pDispInfo->item.lParam;
  450. switch(pDispInfo->item.iSubItem)
  451. {
  452. case c_TopicColumn: // Topic
  453. hr = m_pResults[i].pTitle->GetTopicName(m_pResults[i].dwTopicNumber, pDispInfo->item.pszText, pDispInfo->item.cchTextMax);
  454. if (FAILED(hr))
  455. _wcsncpy(pDispInfo->item.pszText, GetStringResourceW(IDS_UNTITLED), pDispInfo->item.cchTextMax);
  456. // Tell the ListView to store this string.
  457. pDispInfo->item.mask = pDispInfo->item.mask | LVIF_DI_SETITEM;
  458. break;
  459. case c_LocationColumn: // Location
  460. {
  461. ASSERT(m_bAdvancedSearch);
  462. hr = m_pResults[i].pTitle->GetTopicLocation(m_pResults[i].dwTopicNumber, pDispInfo->item.pszText, pDispInfo->item.cchTextMax);
  463. if (FAILED(hr))
  464. _wcsncpy(pDispInfo->item.pszText, GetStringResourceW(IDS_UNKNOWN), pDispInfo->item.cchTextMax);
  465. // Tell the ListView to store this string.
  466. pDispInfo->item.mask = pDispInfo->item.mask | LVIF_DI_SETITEM;
  467. }
  468. break;
  469. #ifdef _DEBUG
  470. case c_RankColumn: // Rank
  471. // shouldn't have a callback. So Fall on down.
  472. default:
  473. ASSERT(0);
  474. break;
  475. #endif
  476. };
  477. }
  478. }