windows-nt/Source/XPSP1/NT/enduser/stuff/hhctrl/listview.cpp
2020-09-26 16:20:57 +08:00

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
};
}
}