557 lines
16 KiB
C++
557 lines
16 KiB
C++
// Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved.
|
|
|
|
#include "header.h"
|
|
#include "fts.h"
|
|
|
|
//#include <commctrl.h>
|
|
#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<LPARAM>(&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<NM_LISTVIEW*>(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<LPARAM>(&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<RESULTSSORTINFO*>(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
|
|
};
|
|
}
|
|
}
|