// Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved. #include "header.h" #include "fts.h" //#include #include "cctlww.h" #include "hhtypes.h" #include "parserhh.h" #include "collect.h" #include "toc.h" #include "system.h" #include "listview.h" #include "secwin.h" // for DEFAULT_NAV_WIDTH; /////////////////////////////////////////////////////////// // // Constants // const int c_TopicColumn = 0; const int c_LocationColumn = 1; const int c_RankColumn = 2; /////////////////////////////////////////////////////////// // // ListViewCompareProc - Used to sort columns in Advanced Search UI mode. // int CALLBACK ListViewCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort); /////////////////////////////////////////////////////////// // // // typedef struct tag_RESULTSSORTINFO { CFTSListView* pThis; // The ListView object controlling the sort. int iSubItem; // column we are sorting. LCID lcid; // locale to sort by WCHAR* pwszUntitled; WCHAR* pwszUnknown; } RESULTSSORTINFO; /////////////////////////////////////////////////////////// // // Constructor // CFTSListView::CFTSListView(CExCollection* pTitleCollection, HWND hwndListView, bool bAdvancedSearch) { m_bSizeColumnsInit = false; m_pTitleCollection = pTitleCollection; m_bAdvancedSearch = bAdvancedSearch; if ( hwndListView != NULL ) { m_hwndListView = hwndListView; m_fInitialized = TRUE; W_EnableUnicode(hwndListView, W_ListView); //---Set the column headings if (bAdvancedSearch) { // Add the columns LV_COLUMNW column; column.mask = LVCF_FMT | LVCF_TEXT; if(g_fBiDi) column.fmt = LVCFMT_RIGHT; else column.fmt = LVCFMT_LEFT; // Title Column column.pszText = (PWSTR)GetStringResourceW(IDS_ADVSEARCH_HEADING_TITLE); int iCol = c_TopicColumn; int iResult = W_ListView_InsertColumn(hwndListView, iCol++, &column); ASSERT(iResult != -1); // Bidi - this is necessary to get the LVCFMT_RIGHT formatting to stick if(g_fBiDi) { column.mask = LVCF_FMT; column.fmt = LVCFMT_RIGHT; SendMessage(hwndListView,LVM_SETCOLUMN, (WPARAM) 0, (LPARAM) &column); } column.mask = LVCF_FMT | LVCF_TEXT; // Location column column.pszText = (PWSTR)GetStringResourceW(IDS_ADVSEARCH_HEADING_LOCATION); iResult = W_ListView_InsertColumn(hwndListView, iCol++, &column); ASSERT(iResult != -1); // Rank Column. column.pszText = (PWSTR)GetStringResourceW(IDS_ADVSEARCH_HEADING_RANK); iResult = W_ListView_InsertColumn(hwndListView, iCol++, &column); ASSERT(iResult != -1); // We want most of the space to be taken up by first column. // Set the columns up for autosizing. W_ListView_SetColumnWidth(hwndListView, c_RankColumn, LVSCW_AUTOSIZE); W_ListView_SetColumnWidth(hwndListView, c_LocationColumn, LVSCW_AUTOSIZE); W_ListView_SetColumnWidth(hwndListView, c_TopicColumn, LVSCW_AUTOSIZE_USEHEADER); // Get the default width of the client. Assumes that the dialog is the default size at startup. RECT client; GetClientRect(hwndListView, &client); m_cxDefault = client.right - client.left; } else { RECT rcTemp; UINT TopicWidth; if (GetWindowRect(m_hwndListView, &rcTemp)) TopicWidth = rcTemp.right - rcTemp.left; else TopicWidth = 122; LV_COLUMNW column; column.mask = LVCF_FMT | LVCF_WIDTH; column.cx = TopicWidth-5; if(g_fBiDi) column.fmt = LVCFMT_RIGHT; else column.fmt = LVCFMT_LEFT; W_ListView_InsertColumn( m_hwndListView, 0, &column ); // Bidi - this is necessary to get the LVCFMT_RIGHT formatting to stick if(g_fBiDi) { column.mask = LVCF_FMT; column.fmt = LVCFMT_RIGHT; SendMessage(m_hwndListView,LVM_SETCOLUMN, (WPARAM) 0, (LPARAM) &column); } } } else m_fInitialized = FALSE; m_pResults = NULL; m_ItemNumber = -1; m_cResultCount = 0; } /////////////////////////////////////////////////////////// // // SetResults // void CFTSListView::SetResults(int cResultCount, SEARCH_RESULT * SearchResults ) { ASSERT(cResultCount >0); ASSERT(SearchResults); m_cResultCount = cResultCount; m_pResults = SearchResults; } /////////////////////////////////////////////////////////// // // AddItem // void CFTSListView::AddItems() { ASSERT(m_cResultCount >0); ASSERT(m_pResults); LV_ITEMW item; // To add to the list view. int i; WCHAR szRank[6]; W_ListView_DeleteAllItems(m_hwndListView); W_ListView_SetItemCount( m_hwndListView, m_cResultCount ); int slot=0; int rank = 1; for ( i=0; i< m_cResultCount; i++) { // need to get the topic string from the Topic Number // Add the Topic string to the List View. Actually using callbacks. // item.pszText = LPSTR_TEXTCALLBACKW; item.mask = LVIF_TEXT|LVIF_PARAM; item.iImage = 0; item.state = 0; item.stateMask = 0; item.iItem = slot; item.iSubItem = c_TopicColumn; item.lParam = i; W_ListView_InsertItem( m_hwndListView, &item ); //--- Two more columns in Adv FTS mode. if (m_bAdvancedSearch) { //--- Add Location Column W_ListView_SetItemText(m_hwndListView, slot, c_LocationColumn, LPSTR_TEXTCALLBACKW); // Has an internal item struct. //--- Add Rank column //wsprintf(szRank,"%d",rank++); if(g_langSystem == LANG_ARABIC || g_langSystem == LANG_HEBREW) { szRank[0]=0x200E; _itow(rank++, szRank+1, 10); } else _itow(rank++, szRank, 10); W_ListView_SetItemText(m_hwndListView, slot, c_RankColumn, szRank); // Has an internal item struct. } slot++; } // fix for 7024 which was a regression caused by AFTS, we must manually sort rather then using the sort bit if (!m_bAdvancedSearch) { // Get the string for untitled things. CWStr wstrUntitled(IDS_UNTITLED); CWStr wstrUnknown(IDS_UNKNOWN); // Fill this structure to make the sorting quicker/more efficient. RESULTSSORTINFO Info; Info.pThis = this; Info.iSubItem = 0; Info.lcid = LOCALE_SYSTEM_DEFAULT; Info.pwszUntitled = wstrUntitled; Info.pwszUnknown = wstrUnknown; W_ListView_SortItems(m_hwndListView, ListViewCompareProc, reinterpret_cast(&Info)); } W_ListView_SetItemState( m_hwndListView, 0, LVIS_SELECTED, LVIF_STATE | LVIS_SELECTED ); } void CFTSListView::ResetQuery(void) { if(m_pResults != NULL ) { // Free the results list // m_pTitleCollection->m_pFullTextSearch->FreeResults( m_pResults ); m_pResults = NULL; m_cResultCount = 0; m_ItemNumber = -1; W_ListView_DeleteAllItems( m_hwndListView ); } } /////////////////////////////////////////////////////////// // // ListViewMsg - Message notification handler. // LRESULT CFTSListView::ListViewMsg(HWND hwnd, NM_LISTVIEW* lParam) { DWORD dwTemp; CExTitle* pTitle; switch(lParam->hdr.code) { case NM_DBLCLK: case NM_RETURN: if ( m_ItemNumber == -1 ) break; dwTemp = m_pResults[m_ItemNumber].dwTopicNumber; pTitle = m_pResults[m_ItemNumber].pTitle; if ( pTitle ) { char szURL[MAX_URL]; if ( (pTitle->GetTopicURL(dwTemp, szURL, sizeof(szURL)) == S_OK) ) ChangeHtmlTopic(szURL, hwnd, 1); } break; case LVN_ITEMCHANGING: if ( ((NM_LISTVIEW*)lParam)->uNewState & LVIS_SELECTED ) { // use the item number as an index into the search results array. // Then use the Topic number to get to the URL to display the Topic. if( m_pResults != NULL ) { m_ItemNumber = (int)((NM_LISTVIEW*)lParam)->lParam; } } else { // HHBUG 2208 - Need to unmark the item if its not selected. m_ItemNumber = -1; } break; case LVN_GETDISPINFOA: OnGetDispInfo((LV_DISPINFO*)lParam); break; case LVN_GETDISPINFOW: OnGetDispInfoW((LV_DISPINFOW*)lParam); break; case LVN_COLUMNCLICK: if (m_bAdvancedSearch) { CHourGlass waitcur; NM_LISTVIEW *pNM = reinterpret_cast(lParam); // Get the string for untitled things. CWStr wstrUntitled(IDS_UNTITLED); CWStr wstrUnknown(IDS_UNKNOWN); // Fill this structure to make the sorting quicker/more efficient. RESULTSSORTINFO Info; Info.pThis = this; Info.iSubItem = pNM->iSubItem; Info.lcid = LOCALE_SYSTEM_DEFAULT; Info.pwszUntitled = wstrUntitled; Info.pwszUnknown = wstrUnknown; W_ListView_SortItems(pNM->hdr.hwndFrom, ListViewCompareProc, reinterpret_cast(&Info)); } // Fall through... default: ; } return 0; } /////////////////////////////////////////////////////////// // // ListViewCompareProc - Used to sort columns in Advanced Search UI mode. // int CALLBACK ListViewCompareProc( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort ) { WCHAR wsz1[MAX_TOPIC_NAME]; WCHAR wsz2[MAX_TOPIC_NAME]; int iReturn; int index1 = (int)lParam1; int index2 = (int)lParam2; RESULTSSORTINFO* pInfo = reinterpret_cast(lParamSort); SEARCH_RESULT* pResults = pInfo->pThis->m_pResults; switch( pInfo->iSubItem ) { case c_TopicColumn: // Topic String pResults[index1].pTitle->GetTopicName( pResults[index1].dwTopicNumber, wsz1, MAX_TOPIC_NAME); if (0 == wsz1[0]) _wcsncpy(wsz1, pInfo->pwszUntitled, MAX_TOPIC_NAME); pResults[index2].pTitle->GetTopicName( pResults[index2].dwTopicNumber, wsz2, MAX_TOPIC_NAME); if (0 == wsz2[0]) _wcsncpy(wsz2, pInfo->pwszUntitled, MAX_TOPIC_NAME); iReturn = W_CompareString(pInfo->lcid, 0, wsz1, -1, wsz2, -1) - 2; break; case c_LocationColumn: // Location String pResults[index1].pTitle->GetTopicLocation( pResults[index1].dwTopicNumber, wsz1, MAX_TOPIC_NAME); if (0 == wsz1[0]) _wcsncpy(wsz1, pInfo->pwszUnknown, MAX_TOPIC_NAME); pResults[index2].pTitle->GetTopicLocation( pResults[index2].dwTopicNumber, wsz2, MAX_TOPIC_NAME); if (0 == wsz2[0]) _wcsncpy(wsz2, pInfo->pwszUnknown, MAX_TOPIC_NAME); iReturn = W_CompareString(pInfo->lcid, 0, wsz1, -1, wsz2, -1) - 2; break; case c_RankColumn: // Rank Number iReturn = index1 - index2; break; default: ASSERT(0); iReturn = index1 - index2; break; } return iReturn; } /////////////////////////////////////////////////////////// // // SizeColumns // void CFTSListView::SizeColumns() { //--- Get the size of the client area RECT rcListView; ::GetClientRect(m_hwndListView, &rcListView); int width = (rcListView.right-rcListView.left); if(!m_bAdvancedSearch) { W_ListView_SetColumnWidth(m_hwndListView, 0, width); return; } //--- So some first time initialization if (!m_bSizeColumnsInit) { m_bSizeColumnsInit = true; // The following little hack should get us the minimun width for the location and rank column. W_ListView_SetColumnWidth(m_hwndListView, c_TopicColumn, width); W_ListView_SetColumnWidth(m_hwndListView, c_LocationColumn, LVSCW_AUTOSIZE_USEHEADER); W_ListView_SetColumnWidth(m_hwndListView, c_RankColumn, LVSCW_AUTOSIZE_USEHEADER); m_cxLocMin = W_ListView_GetColumnWidth(m_hwndListView, 1); m_cxRankMin = W_ListView_GetColumnWidth(m_hwndListView, 2); } //--- Calculate the column widths int cxTitle; // Width of the Title Column. int cxLoc; // Width of the location Column. int cxRank = m_cxRankMin; // Width of the Rank Column. if (width >= m_cxDefault) { // Everything is fully visible. cxTitle = width / 2; cxLoc = (width - cxTitle - cxRank); } else if ((width < m_cxDefault) /*&& (width > DEFAULT_NAV_WIDTH/2)*/) { // Only part of Rank column is shown and only the min loc size is used. cxTitle = width / 2; cxLoc = (width - cxTitle)* 3 /4; if (cxLoc > m_cxLocMin) { // Make sure that we use the min width for the location. cxTitle += cxLoc - m_cxLocMin; // Add the difference back to the Title column. cxLoc = m_cxLocMin; } } /* This branch isn't needed because the pane itself doesn't size below this medium. else if (width <= DEFAULT_NAV_WIDTH/2) { // No Rank is shown. Partial Location is shown. cxTitle = width * 3/4; cxLoc = m_cxLocMin; } */ W_ListView_SetColumnWidth(m_hwndListView, c_RankColumn, cxRank); W_ListView_SetColumnWidth(m_hwndListView, c_LocationColumn, cxLoc); W_ListView_SetColumnWidth(m_hwndListView, c_TopicColumn, cxTitle); } /////////////////////////////////////////////////////////// // // OnGetDispInfo // void CFTSListView::OnGetDispInfo(LV_DISPINFOA* pDispInfo) { static char szTemp[MAX_PATH*4]; // Holds strings. // Check to see if we have results. if ( m_pResults != NULL ) { int i = (int)pDispInfo->item.lParam; switch(pDispInfo->item.iSubItem) { case c_TopicColumn: // Topic m_pResults[i].pTitle->GetTopicName( m_pResults[i].dwTopicNumber, pDispInfo->item.pszText, pDispInfo->item.cchTextMax ); if (!pDispInfo->item.pszText[0]) { strncpy(pDispInfo->item.pszText, GetStringResource(IDS_UNTITLED), pDispInfo->item.cchTextMax); } // Tell the ListView to store this string. pDispInfo->item.mask = pDispInfo->item.mask | LVIF_DI_SETITEM; break; case c_LocationColumn: // Location { ASSERT(m_bAdvancedSearch); HRESULT hr = m_pResults[i].pTitle->GetTopicLocation(m_pResults[i].dwTopicNumber, szTemp, MAX_PATH*4); if (FAILED(hr)) { strcpy(szTemp, GetStringResource(IDS_UNKNOWN)); } strncpy(pDispInfo->item.pszText, szTemp, pDispInfo->item.cchTextMax); // Tell the ListView to store this string. pDispInfo->item.mask = pDispInfo->item.mask | LVIF_DI_SETITEM; } break; #ifdef _DEBUG case c_RankColumn: // Rank // shouldn't have a callback. So Fall on down. default: ASSERT(0); break; #endif }; } } /////////////////////////////////////////////////////////// // // OnGetDispInfo // void CFTSListView::OnGetDispInfoW(LV_DISPINFOW* pDispInfo) { HRESULT hr; // Check to see if we have results. if ( m_pResults != NULL ) { int i = (int)pDispInfo->item.lParam; switch(pDispInfo->item.iSubItem) { case c_TopicColumn: // Topic hr = m_pResults[i].pTitle->GetTopicName(m_pResults[i].dwTopicNumber, pDispInfo->item.pszText, pDispInfo->item.cchTextMax); if (FAILED(hr)) _wcsncpy(pDispInfo->item.pszText, GetStringResourceW(IDS_UNTITLED), pDispInfo->item.cchTextMax); // Tell the ListView to store this string. pDispInfo->item.mask = pDispInfo->item.mask | LVIF_DI_SETITEM; break; case c_LocationColumn: // Location { ASSERT(m_bAdvancedSearch); hr = m_pResults[i].pTitle->GetTopicLocation(m_pResults[i].dwTopicNumber, pDispInfo->item.pszText, pDispInfo->item.cchTextMax); if (FAILED(hr)) _wcsncpy(pDispInfo->item.pszText, GetStringResourceW(IDS_UNKNOWN), pDispInfo->item.cchTextMax); // Tell the ListView to store this string. pDispInfo->item.mask = pDispInfo->item.mask | LVIF_DI_SETITEM; } break; #ifdef _DEBUG case c_RankColumn: // Rank // shouldn't have a callback. So Fall on down. default: ASSERT(0); break; #endif }; } }