2236 lines
70 KiB
C++
2236 lines
70 KiB
C++
// Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved.
|
|
|
|
#include "header.h"
|
|
#include "hhctrl.h"
|
|
#include "strtable.h"
|
|
#include "resource.h"
|
|
#include "hha_strtable.h"
|
|
#include "onclick.h"
|
|
#include "index.h"
|
|
#include "toc.h"
|
|
#include "wwheel.h"
|
|
#include "web.h"
|
|
#include <shellapi.h>
|
|
#include <wininet.h>
|
|
|
|
#include "sample.h"
|
|
#include "subset.h"
|
|
#include "secwin.h" //For extern CHHWinType** pahwnd;
|
|
|
|
#undef WINUSERAPI
|
|
#define WINUSERAPI
|
|
#include "htmlhelp.h"
|
|
|
|
#include "lasterr.h" // Support for reporting the last error.
|
|
|
|
#define NOTEXT_BTN_HEIGHT 12
|
|
#define NOTEXT_BTN_WIDTH 12
|
|
#define CXBUTTONEXTRA 8 // spacing between text and button
|
|
#define CYBUTTONEXTRA 8
|
|
|
|
static const char txtOpen[] = "open";
|
|
static const char txtCplOpen[] = "cplopen";
|
|
static const char txtRegSection[] = "Software\\Microsoft\\HtmlHelp\\";
|
|
static const char txtShortcut[] = "shortcut";
|
|
|
|
static DWORD GetTextDimensions(HWND hwnd, PCSTR psz, HFONT hfont = NULL);
|
|
|
|
HRESULT OSLangMatchesChm(CExCollection *pCollection);
|
|
|
|
HRESULT GetWordWheelHits( PSTR pszKeyword, CWordWheel* pWordWheel,
|
|
CTable* ptblURLs, CWTable* ptblTitles,
|
|
CWTable *ptblLocations, BOOL bTestMode,
|
|
BOOL bSkipCurrent, BOOL bFullURL = FALSE );
|
|
|
|
HRESULT GetWordWheelHits( CExCollection* pCollection,
|
|
CTable* ptblItems, CTable* ptblURLs,
|
|
CWTable* ptblTitles, CWTable *ptblLocations,
|
|
BOOL bKLink, BOOL bTestMode, BOOL bSkipCurrent );
|
|
|
|
HRESULT OnWordWheelLookup( CTable* ptblItems, CExCollection* pExCollection,
|
|
PCSTR pszDefaultTopic = NULL, POINT* ppt = NULL,
|
|
HWND hWndParent = NULL, BOOL bDialog = TRUE,
|
|
BOOL bKLink = TRUE,
|
|
BOOL bTestMode = FALSE, BOOL bSkipCurrent = FALSE,
|
|
BOOL bAlwaysShowList = FALSE, BOOL bAlphaSortHits = TRUE,
|
|
PCSTR pszWindow = NULL);
|
|
|
|
BOOL g_HackForBug_HtmlHelpDB_1884 = 0;
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: OnWordWheelLookup
|
|
|
|
PURPOSE: Given a keyword, or semi-colon delimited list of keywords,
|
|
find the hits. If no hits found display a "not found" message;
|
|
if one hit found then just jump to the topic, otherwise
|
|
display a list of topics for the user to choose from.
|
|
|
|
PARAMETERS:
|
|
pszKeywords - word(s) to lookup
|
|
|
|
... the rest the same as OnWordWheelLookup( CTable* ... )
|
|
|
|
RETURNS:
|
|
|
|
TRUE if there is at least one match. FALSE otherwise.
|
|
|
|
COMMENTS:
|
|
|
|
No support for external titles with this API.
|
|
|
|
MODIFICATION DATES:
|
|
09-Jan-1998 [paulti]
|
|
|
|
***************************************************************************/
|
|
|
|
HRESULT OnWordWheelLookup( PSTR pszKeywords, CExCollection* pExCollection,
|
|
PCSTR pszDefaultTopic, POINT* ppt,
|
|
HWND hWndParent, BOOL bDialog, BOOL bKLink,
|
|
BOOL bTestMode, BOOL bSkipCurrent,
|
|
BOOL bAlwaysShowList, BOOL bAlphaSortHits,
|
|
PCSTR pszWindow)
|
|
{
|
|
// trim leading and trailing spaces
|
|
char* pszKeywords2 = new char[strlen(pszKeywords)+1];
|
|
strcpy( pszKeywords2, pszKeywords );
|
|
SzTrimSz( pszKeywords2 );
|
|
|
|
// create our lists
|
|
CTable tblItems;
|
|
|
|
// initialize our item list
|
|
tblItems.AddString( "" ); // set item1 to NULL -- no external titles
|
|
PSTR pszKeyword = StrToken( pszKeywords2, ";" );
|
|
while( pszKeyword ) {
|
|
CHAR szKeyword[HHWW_MAX_KEYWORD_LENGTH+1];
|
|
lstrcpyn( szKeyword, pszKeyword, sizeof(szKeyword)-1 );
|
|
SzTrimSz( szKeyword );
|
|
tblItems.AddString( szKeyword );
|
|
pszKeyword = StrToken(NULL, ";");
|
|
}
|
|
delete [] pszKeywords2;
|
|
|
|
return OnWordWheelLookup( &tblItems, pExCollection,
|
|
pszDefaultTopic, ppt, hWndParent, bDialog,
|
|
bKLink, bTestMode, bSkipCurrent,
|
|
bAlwaysShowList, bAlphaSortHits, pszWindow );
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: OnWordWheelLookup
|
|
|
|
PURPOSE: Given a list of keywords find the hits. If no hits found
|
|
display a "not found" message if one hit found then just
|
|
jump to the topic, otherwise display a list of topics for
|
|
the user to choose from.
|
|
|
|
PARAMETERS:
|
|
ptblItems - word(s) to lookup (first item is semi-colon delimited
|
|
list of external titles to check).
|
|
pCollection - collection pointer, needed to access word wheels.
|
|
pszDefaultTopic - default topic to jump to.
|
|
ppt - pointer to a point to center the window on. If NULL
|
|
then we will use the current mouse pointer position.
|
|
hWndParent - window to parent the "Topics Found" dialog/menu to.
|
|
If NULL, then use the active window.
|
|
bDialog - dialog based? If not, use a menu.
|
|
bKLink - is this a keyword lookup? if not, use the ALink list.
|
|
bTestMode - test existence only (dont' show a UI).
|
|
bSkipCurrent - skip the current URL in the returned list.
|
|
bAlwaysShowList - always show the hit list even if only one topic is found.
|
|
bAlphaSortHits - alpha sort the title list or not.
|
|
|
|
|
|
RETURNS:
|
|
|
|
TRUE if there is at least one match. FALSE otherwise.
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
09-Jan-1998 [paulti]
|
|
|
|
***************************************************************************/
|
|
|
|
HRESULT OnWordWheelLookup( CTable* ptblItems, CExCollection* pCollection,
|
|
PCSTR pszDefaultTopic, POINT* ppt,
|
|
HWND hWndParent, BOOL bDialog, BOOL bKLink,
|
|
BOOL bTestMode, BOOL bSkipCurrent,
|
|
BOOL bAlwaysShowList, BOOL bAlphaSortHits, PCSTR pszWindow)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
UINT CodePage = pCollection ? pCollection->GetMasterTitle()->GetInfo()->GetCodePage() : CP_ACP;
|
|
|
|
// create our lists
|
|
CWTable tblTitles( CodePage );
|
|
CWTable tblLocations( CodePage );
|
|
CTable tblURLs;
|
|
|
|
if( pCollection ) {
|
|
|
|
// get the active window if non specified
|
|
if( !hWndParent )
|
|
hWndParent = GetActiveWindow();
|
|
|
|
// get current mouse pointer position if non-specified
|
|
POINT pt;
|
|
if( !ppt ) {
|
|
GetCursorPos( &pt );
|
|
ppt = &pt;
|
|
#if 1 // reverted bug fix #5516
|
|
HWND hwnd = GetFocus();
|
|
if ( hwnd ) {
|
|
DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE );
|
|
if ( dwStyle & BS_NOTIFY )
|
|
{
|
|
|
|
RECT rc;
|
|
if( GetWindowRect(hwnd, &rc) ) {
|
|
pt.y = rc.top+(RECT_WIDTH(rc)/2);
|
|
pt.x = rc.left + ((RECT_HEIGHT(rc)/3)*2); // two-thirds of the height of the button
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
hr = GetWordWheelHits( pCollection,
|
|
ptblItems, &tblURLs, &tblTitles, &tblLocations,
|
|
bKLink, bTestMode, bSkipCurrent );
|
|
}
|
|
else {
|
|
hr = HH_E_KEYWORD_NOT_SUPPORTED;
|
|
}
|
|
|
|
// we are all done if we are just in test mode
|
|
if( bTestMode )
|
|
return hr;
|
|
|
|
int iIndex = 0;
|
|
char szURL[INTERNET_MAX_URL_LENGTH];
|
|
|
|
// if we get no topics then display the default message
|
|
// othewise an "error" message
|
|
if( FAILED(hr) || tblURLs.CountStrings() < 1) {
|
|
|
|
if( pCollection && pszDefaultTopic ) {
|
|
if( pCollection && StrRChr( pszDefaultTopic, ':' ) == NULL ) {
|
|
CStr szCurrentURL;
|
|
GetCurrentURL( &szCurrentURL, hWndParent );
|
|
CStr szFileName;
|
|
LPSTR pszColon = StrRChr( szCurrentURL.psz, ':' );
|
|
LPSTR pszSlash = StrRChr( szCurrentURL.psz, '/' );
|
|
LPSTR pszTail = max( pszColon, pszSlash );
|
|
lstrcpyn( szURL, szCurrentURL.psz, (int)(pszTail - szCurrentURL.psz +2) );
|
|
strcat( szURL, pszDefaultTopic );
|
|
}
|
|
else
|
|
strcpy( szURL, pszDefaultTopic );
|
|
hr = S_OK; // set to S_OK so the jump below will work
|
|
}
|
|
else {
|
|
int iStr = 0;
|
|
|
|
switch( hr ) {
|
|
|
|
case HH_E_KEYWORD_NOT_FOUND:
|
|
iStr = IDS_HH_E_KEYWORD_NOT_FOUND;
|
|
break;
|
|
|
|
case HH_E_KEYWORD_IS_PLACEHOLDER:
|
|
iStr = IDS_HH_E_KEYWORD_IS_PLACEHOLDER;
|
|
break;
|
|
|
|
case HH_E_KEYWORD_NOT_IN_SUBSET:
|
|
iStr = IDS_HH_E_KEYWORD_NOT_IN_SUBSET;
|
|
break;
|
|
|
|
case HH_E_KEYWORD_NOT_IN_INFOTYPE:
|
|
iStr = IDS_HH_E_KEYWORD_NOT_IN_INFOTYPE;
|
|
break;
|
|
|
|
case HH_E_KEYWORD_EXCLUDED:
|
|
iStr = IDS_HH_E_KEYWORD_EXCLUDED;
|
|
break;
|
|
|
|
case HH_E_KEYWORD_NOT_SUPPORTED:
|
|
iStr = IDS_REQUIRES_HTMLHELP;
|
|
break;
|
|
|
|
default:
|
|
iStr = IDS_IDH_MISSING_CONTEXT;
|
|
break;
|
|
|
|
}
|
|
MsgBox(iStr, MB_OK | MB_ICONWARNING | MB_SETFOREGROUND);
|
|
}
|
|
}
|
|
else {
|
|
// if only one topic then jump to it
|
|
if( !bAlwaysShowList && tblURLs.CountStrings() == 1 ) {
|
|
tblURLs.GetString( szURL, 1 );
|
|
}
|
|
else {
|
|
|
|
// we can sort the title table since it contains the index value
|
|
// of the associated URL so just make sure to always fetch the
|
|
// URL index from the selected title string and use that to get the URL
|
|
if( bAlphaSortHits ) {
|
|
tblTitles.SetSorting(GetSystemDefaultLCID());
|
|
tblTitles.SortTable(sizeof(HASH));
|
|
}
|
|
if( !bDialog && tblURLs.CountStrings() < 20 ) {
|
|
HMENU hMenu = CreatePopupMenu();
|
|
if( hMenu ) {
|
|
for( int i = 1; i <= tblURLs.CountStrings(); i++ ) {
|
|
LPSTR psz = tblTitles.GetHashStringPointer(i);
|
|
// if title too long, truncate it (511 seems like a good max)
|
|
if( psz && *psz ) {
|
|
int iLen = (int)strlen(psz);
|
|
#define MAX_LEN 511
|
|
char sz[MAX_LEN+1];
|
|
if( iLen >= MAX_LEN ) {
|
|
strncpy( sz, psz, MAX_LEN-1 );
|
|
sz[MAX_LEN] = 0;
|
|
psz = sz;
|
|
}
|
|
}
|
|
HxAppendMenu(hMenu, MF_STRING, IDM_RELATED_TOPIC + i, psz );
|
|
}
|
|
int iCmd = TrackPopupMenu(hMenu,
|
|
TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON |
|
|
TPM_NONOTIFY | TPM_RETURNCMD,
|
|
ppt->x, ppt->y, 0, hWndParent, NULL);
|
|
DestroyMenu( hMenu );
|
|
if( iCmd ) {
|
|
iIndex = tblTitles.GetInt( iCmd - IDM_RELATED_TOPIC );
|
|
tblURLs.GetString( szURL, iIndex );
|
|
}
|
|
else {
|
|
hr = HH_E_KEYWORD_NOT_FOUND; // Means we have nothing to jump to
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HFONT hfont;
|
|
if ( pCollection )
|
|
hfont = pCollection->m_phmData->GetContentFont();
|
|
else
|
|
hfont = _Resource.GetUIFont(); // Not ideal but will have to do.
|
|
|
|
UINT CodePage = pCollection ? pCollection->GetMasterTitle()->GetInfo()->GetCodePage() : CP_ACP;
|
|
|
|
CTopicList TopicList( hWndParent, &tblTitles, hfont, &tblLocations );
|
|
if( TopicList.DoModal() ) {
|
|
iIndex = tblTitles.GetInt( TopicList.m_pos );
|
|
tblURLs.GetString( szURL, iIndex );
|
|
}
|
|
else {
|
|
hr = HH_E_KEYWORD_NOT_FOUND; // Means we have nothing to jump to
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we found something to jump to then jump to it
|
|
if( !FAILED(hr) )
|
|
{
|
|
if (pCollection && pszWindow && pszWindow[0])
|
|
{
|
|
CStr cszPrefix = szURL;
|
|
|
|
// Is the window we are attempting to open defined by the master CHM?
|
|
CHHWinType* phh = FindWindowType(pszWindow, NULL, pCollection->GetPathName());
|
|
// Does this window actually exist?
|
|
if (phh && IsWindow(phh->hwndHelp))
|
|
{
|
|
doHHWindowJump(cszPrefix, phh->hwndHelp);
|
|
return hr;
|
|
}
|
|
|
|
cszPrefix += ">";
|
|
cszPrefix += pszWindow;
|
|
OnDisplayTopic(hWndParent, cszPrefix, 0);
|
|
}
|
|
else
|
|
doHHWindowJump( szURL, hWndParent );
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: GetWordWheelHits
|
|
|
|
PURPOSE: Given a single keyword, find the hits
|
|
Return S_OK if there is at least one found hit
|
|
|
|
PARAMETERS:
|
|
pWordWheel - word wheel to look in
|
|
ptblURLS - URL list
|
|
ptblTitles - title list
|
|
bTestMode - test existence only (replaces old TestAKLink code)
|
|
bSkipCurrent - skip the current URL in the returned list
|
|
bFullURL - return a URL with a full pathname to the title or (default)
|
|
just return the URL with just the filename of the title
|
|
|
|
RETURNS:
|
|
|
|
S_OK - hits were found.
|
|
HH_E_KEYWORD_NOT_FOUND - no hits found.
|
|
HH_E_KEYWORD_IS_PLACEHOLDER - keyword is a placeholder or
|
|
a "runaway" see also.
|
|
HH_E_KEYWORD_NOT_IN_SUBSET - no hits found due to subset
|
|
exclusion.
|
|
HH_E_KEYWORD_NOT_IN_INFOTYPE - no hits found due to infotype
|
|
exclusion.
|
|
HH_E_KEYWORD_EXCLUDED - no hits found due to infotype
|
|
and subset exclusion.
|
|
|
|
HH_E_KEYWORD_NOT_SUPPORTED - keywords not supported in this mode.
|
|
|
|
COMMENTS:
|
|
|
|
HH_E_KEYWORD_EXCLUDED is returned only when no hits are found due to
|
|
*both* removal by subsetting and infotype
|
|
|
|
MODIFICATION DATES:
|
|
13-Apr-1998 [paulti]
|
|
|
|
***************************************************************************/
|
|
|
|
HRESULT GetWordWheelHits( PSTR pszKeyword, CWordWheel* pWordWheel,
|
|
CTable* ptblURLs, CWTable* ptblTitles, CWTable *ptblLocations,
|
|
BOOL bTestMode, BOOL bSkipCurrent, BOOL bFullURL )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL bExcludedBySubset = FALSE;
|
|
BOOL bExcludedByInfoType = FALSE;
|
|
BOOL bPlaceHolder = FALSE;
|
|
|
|
CStructuralSubset* pSubset;
|
|
|
|
if( pszKeyword ) {
|
|
|
|
DWORD dwIndexLast = HHWW_ERROR;
|
|
DWORD dwIndexFirst = pWordWheel->GetIndex( pszKeyword, FALSE, &dwIndexLast );
|
|
|
|
if( dwIndexFirst == HHWW_ERROR )
|
|
return HH_E_KEYWORD_NOT_FOUND;
|
|
|
|
for( DWORD dwIndex = dwIndexFirst; dwIndex <= dwIndexLast; dwIndex++ ) {
|
|
|
|
// skip over the placeholders
|
|
if( pWordWheel->IsPlaceHolder( dwIndex ) ) {
|
|
bPlaceHolder = TRUE;
|
|
continue;
|
|
}
|
|
|
|
// follow the see also links
|
|
CHAR szSeeAlso[HHWW_MAX_KEYWORD_LENGTH+1];
|
|
if( pWordWheel->GetSeeAlso( dwIndex, szSeeAlso, sizeof(szSeeAlso) ) ) {
|
|
if( pWordWheel->AddRef() >= HHWW_MAX_LEVELS ) {
|
|
pWordWheel->Release();
|
|
return HH_E_KEYWORD_IS_PLACEHOLDER;
|
|
}
|
|
hr = GetWordWheelHits( szSeeAlso, pWordWheel, ptblURLs, ptblTitles, ptblLocations,
|
|
bTestMode, bSkipCurrent, bFullURL );
|
|
pWordWheel->Release();
|
|
|
|
switch( hr ) {
|
|
case HH_E_KEYWORD_EXCLUDED:
|
|
bExcludedBySubset = TRUE;
|
|
bExcludedByInfoType = TRUE;
|
|
break;
|
|
|
|
case HH_E_KEYWORD_NOT_IN_SUBSET:
|
|
bExcludedBySubset = TRUE;
|
|
break;
|
|
|
|
case HH_E_KEYWORD_NOT_IN_INFOTYPE:
|
|
bExcludedByInfoType = TRUE;
|
|
break;
|
|
|
|
case HH_E_KEYWORD_IS_PLACEHOLDER:
|
|
bPlaceHolder = TRUE;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// fetch the hits
|
|
CStr cszCurrentURL;
|
|
GetCurrentURL( &cszCurrentURL );
|
|
DWORD dwHitCount = pWordWheel->GetHitCount(dwIndex);
|
|
if (dwHitCount != HHWW_ERROR) {
|
|
for (DWORD i = 0; i < dwHitCount; i++) {
|
|
CExTitle* pTitle = NULL;
|
|
DWORD dwURLId = pWordWheel->GetHit(dwIndex, i, &pTitle);
|
|
if (pTitle && dwURLId != HHWW_ERROR) {
|
|
|
|
#if 0 // we do not support infotypes currently
|
|
CSubSet* pSS;
|
|
const unsigned int *pdwITBits;
|
|
//
|
|
// Do we need to filter based on infotypes ?
|
|
//
|
|
if ( pTitle->m_pCollection && pTitle->m_pCollection->m_pSubSets &&
|
|
(pSS = pTitle->m_pCollection->m_pSubSets->GetIndexSubset()) && !pSS->m_bIsEntireCollection )
|
|
{
|
|
//
|
|
// Yep, do the filter thang.
|
|
//
|
|
pdwITBits = pTitle->GetTopicITBits(dwURLId);
|
|
if (! pTitle->m_pCollection->m_pSubSets->fIndexFilter(pdwITBits) ) {
|
|
bExcludedByInfoType = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
//
|
|
// Do we need to filter based on structural subsets?
|
|
//
|
|
if( pTitle->m_pCollection && pTitle->m_pCollection->m_pSSList &&
|
|
(pSubset = pTitle->m_pCollection->m_pSSList->GetF1()) && !pSubset->IsEntire() )
|
|
{
|
|
// Yes, filter using the current structural subset for F1.
|
|
//
|
|
if (! pSubset->IsTitleInSubset(pTitle) ) {
|
|
bExcludedBySubset = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// if we make it this far and we are in test mode
|
|
// we can bail out and return S_OK
|
|
if( bTestMode )
|
|
return S_OK;
|
|
|
|
char szTitle[1024];
|
|
szTitle[0] = 0;
|
|
pTitle->GetTopicName( dwURLId, szTitle, sizeof(szTitle) );
|
|
if( !szTitle[0] )
|
|
strcpy( szTitle, GetStringResource( IDS_UNTITLED ) );
|
|
|
|
char szLocation[INTERNET_MAX_PATH_LENGTH]; // 2048 should be plenty
|
|
szLocation[0] = 0;
|
|
if( pTitle->GetTopicLocation(dwURLId, szLocation, INTERNET_MAX_PATH_LENGTH) != S_OK )
|
|
strcpy( szLocation, GetStringResource( IDS_UNKNOWN ) );
|
|
|
|
char szURL[INTERNET_MAX_URL_LENGTH];
|
|
szURL[0] = 0;
|
|
pTitle->GetTopicURL( dwURLId, szURL, sizeof(szURL), bFullURL );
|
|
|
|
char szFullURL[INTERNET_MAX_URL_LENGTH];
|
|
szFullURL[0] = 0;
|
|
pTitle->ConvertURL( szURL, szFullURL );
|
|
|
|
if( szURL[0] ) {
|
|
if( !ptblURLs->IsStringInTable(szURL) ) {
|
|
if( cszCurrentURL.IsEmpty()
|
|
|| !(bSkipCurrent && (lstrcmpi( cszCurrentURL, szURL ) == 0) ||(lstrcmpi( cszCurrentURL, szFullURL ) == 0 ) )) {
|
|
int iIndex = ptblURLs->AddString(szURL);
|
|
ptblTitles->AddIntAndString(iIndex, szTitle[0]?szTitle:"");
|
|
ptblLocations->AddString( *szLocation?szLocation:"" );
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// determine the proper return value
|
|
if( ptblURLs->CountStrings() < 1 ) {
|
|
if( bExcludedBySubset && bExcludedByInfoType )
|
|
hr = HH_E_KEYWORD_EXCLUDED;
|
|
else if( bExcludedBySubset )
|
|
hr = HH_E_KEYWORD_NOT_IN_SUBSET;
|
|
else if( bExcludedByInfoType )
|
|
hr = HH_E_KEYWORD_NOT_IN_INFOTYPE;
|
|
else if( bPlaceHolder )
|
|
hr = HH_E_KEYWORD_IS_PLACEHOLDER;
|
|
else
|
|
hr = HH_E_KEYWORD_NOT_FOUND;
|
|
}
|
|
else
|
|
hr = S_OK;
|
|
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: GetWordWheelHits
|
|
|
|
PURPOSE: Get all the links for the specified keywords
|
|
Return TRUE if there is at least one match
|
|
|
|
PARAMETERS:
|
|
pCollection - point to the collection
|
|
ptblItems - item table, item 1 is the list of external titles
|
|
and items 2 thru N are the list of keywords
|
|
ptblURLs - URL list
|
|
ptblTitles - title list
|
|
bKLink - is this a klink (defaults to alink)
|
|
bTestMode - test existence only (replaces old TestAKLink code)
|
|
bSkipCurrent - skip the current URL in the returned list
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
14-Nov-1997 [paulti]
|
|
|
|
***************************************************************************/
|
|
|
|
HRESULT GetWordWheelHits( CExCollection* pCollection,
|
|
CTable* ptblItems, CTable* ptblURLs, CWTable* ptblTitles, CWTable *ptblLocations,
|
|
BOOL bKLink, BOOL bTestMode, BOOL bSkipCurrent )
|
|
{
|
|
int pos;
|
|
CWordWheel* pWordWheel = NULL;
|
|
HRESULT hr = S_OK;
|
|
HRESULT hrReturn = HH_E_KEYWORD_NOT_FOUND; // assume the worst
|
|
|
|
if( pCollection ) {
|
|
pWordWheel = (bKLink ? pCollection->m_pDatabase->GetKeywordLinks() :
|
|
pCollection->m_pDatabase->GetAssociativeLinks());
|
|
}
|
|
|
|
// if we did not get a word wheel pointer then skip the internal
|
|
// word wheels and fall back to just the external ones
|
|
if( pWordWheel ) {
|
|
|
|
// add in the internal hits first
|
|
for( pos = 2; pos <= ptblItems->CountStrings(); pos++ ) {
|
|
CStr cszKeywords(ptblItems->GetPointer(pos)); // copy so StrToken can modify
|
|
PSTR pszKeyword = StrToken(cszKeywords, ";");
|
|
|
|
while( pszKeyword ) {
|
|
hr = GetWordWheelHits( pszKeyword, pWordWheel,
|
|
ptblURLs, ptblTitles, ptblLocations,
|
|
bTestMode, bSkipCurrent, TRUE );
|
|
|
|
if( bTestMode && !FAILED(hr) )
|
|
return hr;
|
|
|
|
// if we failed again, then collate the resultant error
|
|
if( FAILED( hrReturn ) && FAILED( hr ) ) {
|
|
|
|
switch( hrReturn ) {
|
|
|
|
case HH_E_KEYWORD_NOT_IN_INFOTYPE:
|
|
if( hr == HH_E_KEYWORD_NOT_IN_SUBSET )
|
|
hrReturn = HH_E_KEYWORD_EXCLUDED;
|
|
else
|
|
hrReturn = hr;
|
|
break;
|
|
|
|
case HH_E_KEYWORD_NOT_IN_SUBSET:
|
|
if( hr == HH_E_KEYWORD_NOT_IN_INFOTYPE )
|
|
hrReturn = HH_E_KEYWORD_EXCLUDED;
|
|
else
|
|
hrReturn = hr;
|
|
break;
|
|
|
|
case HH_E_KEYWORD_EXCLUDED:
|
|
hrReturn = HH_E_KEYWORD_EXCLUDED;
|
|
break;
|
|
|
|
default:
|
|
hrReturn = hr;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
else if( !FAILED( hr ) )
|
|
hrReturn = hr;
|
|
|
|
pszKeyword = StrToken(NULL, ";");
|
|
if( pszKeyword )
|
|
pszKeyword = FirstNonSpace(pszKeyword);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// create a list of the external titles
|
|
// skip those titles that are already in the collection
|
|
CStr cszTitle(ptblItems->GetPointer(1));
|
|
if( !IsEmptyString(cszTitle) ) {
|
|
|
|
PSTR pszTitle = StrToken(cszTitle, ";");
|
|
CWTable* ptblTitleFiles = new CWTable(ptblTitles->GetCodePage()); // REVIEW: external titles must have the same codepage!
|
|
while( pszTitle ) {
|
|
if( *pszTitle ) {
|
|
TCHAR szTitle[MAX_PATH];
|
|
PSTR pszTitle2 = szTitle;
|
|
strcpy( szTitle, pszTitle );
|
|
pszTitle2 = FirstNonSpace( szTitle );
|
|
RemoveTrailingSpaces( pszTitle2 );
|
|
CExTitle* pTitle = NULL;
|
|
if( *pszTitle2 && pCollection && FAILED(pCollection->URL2ExTitle(pszTitle2,&pTitle ) ) ) {
|
|
CStr Title;
|
|
// check if the file lives where the master file lives
|
|
if( pszTitle2 ) {
|
|
char szPathName[_MAX_PATH];
|
|
char szFileName[_MAX_FNAME];
|
|
char szExtension[_MAX_EXT];
|
|
SplitPath((LPSTR)pszTitle2, NULL, NULL, szFileName, szExtension);
|
|
char szMasterPath[_MAX_PATH];
|
|
char szMasterDrive[_MAX_DRIVE];
|
|
SplitPath((LPSTR)pCollection->m_csFile, szMasterDrive, szMasterPath, NULL, NULL);
|
|
strcpy( szPathName, szMasterDrive );
|
|
CatPath( szPathName, szMasterPath );
|
|
CatPath( szPathName, szFileName );
|
|
strcat( szPathName, szExtension );
|
|
Title = szPathName;
|
|
if( (GetFileAttributes(szPathName) != HFILE_ERROR) || FindThisFile( NULL, pszTitle2, &Title, FALSE ) ) {
|
|
if( Title.IsNonEmpty() )
|
|
ptblTitleFiles->AddString( Title );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pszTitle = StrToken(NULL, ";");
|
|
}
|
|
|
|
// add in the external hits last
|
|
int iTitleCount = ptblTitleFiles->CountStrings();
|
|
for( int iTitle = 1; iTitle <= iTitleCount; iTitle++ ) {
|
|
char szTitle[MAX_PATH];
|
|
ptblTitleFiles->GetString( szTitle, iTitle );
|
|
|
|
// get title objects
|
|
CExTitle* pTitle = new CExTitle( szTitle, NULL );
|
|
CTitleDatabase* pDatabase = new CTitleDatabase( pTitle );
|
|
|
|
CWordWheel* pWordWheel = NULL;
|
|
if( bKLink )
|
|
pWordWheel = pDatabase->GetKeywordLinks();
|
|
else
|
|
pWordWheel = pDatabase->GetAssociativeLinks();
|
|
|
|
for (int pos = 2; pos <= ptblItems->CountStrings(); pos++) {
|
|
CStr cszKeywords(ptblItems->GetPointer(pos)); // copy so StrToken can modify
|
|
PSTR pszKeyword = StrToken(cszKeywords, ";");
|
|
|
|
while( pszKeyword ) {
|
|
hr = GetWordWheelHits( pszKeyword, pWordWheel,
|
|
ptblURLs, ptblTitles, ptblLocations,
|
|
bTestMode, bSkipCurrent, TRUE );
|
|
|
|
if( bTestMode && !FAILED(hr) ) {
|
|
hrReturn = hr;
|
|
break;
|
|
}
|
|
|
|
// if we failed again, then collate the resultant error
|
|
if( FAILED( hrReturn ) && FAILED( hr ) ) {
|
|
|
|
switch( hrReturn ) {
|
|
|
|
case HH_E_KEYWORD_NOT_IN_INFOTYPE:
|
|
if( hr == HH_E_KEYWORD_NOT_IN_SUBSET )
|
|
hrReturn = HH_E_KEYWORD_EXCLUDED;
|
|
else
|
|
hrReturn = hr;
|
|
break;
|
|
|
|
case HH_E_KEYWORD_NOT_IN_SUBSET:
|
|
if( hr == HH_E_KEYWORD_NOT_IN_INFOTYPE )
|
|
hrReturn = HH_E_KEYWORD_EXCLUDED;
|
|
else
|
|
hrReturn = hr;
|
|
break;
|
|
|
|
case HH_E_KEYWORD_EXCLUDED:
|
|
hrReturn = HH_E_KEYWORD_EXCLUDED;
|
|
break;
|
|
|
|
default:
|
|
hrReturn = hr;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
else if( !FAILED( hr ) )
|
|
hrReturn = hr;
|
|
|
|
pszKeyword = StrToken(NULL, ";");
|
|
if( pszKeyword )
|
|
pszKeyword = FirstNonSpace(pszKeyword);
|
|
}
|
|
|
|
if( bTestMode && !FAILED(hr) ) {
|
|
hrReturn = hr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// free title objects
|
|
delete pDatabase;
|
|
delete pTitle;
|
|
|
|
if( bTestMode && !FAILED(hr) ) {
|
|
hrReturn = hr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// free our title list
|
|
delete ptblTitleFiles;
|
|
}
|
|
|
|
return hrReturn;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: CHtmlHelpControl::OnAKLink
|
|
|
|
PURPOSE: Return TRUE if there is at least one match
|
|
|
|
PARAMETERS:
|
|
fKLink - is this a klink? (defaults to alink)
|
|
bTestMode - test for existence only
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
28-JAN-1998 [paulti] major rewrite
|
|
|
|
***************************************************************************/
|
|
|
|
BOOL CHtmlHelpControl::OnAKLink( BOOL fKLink, BOOL bTestMode )
|
|
{
|
|
if( !m_ptblItems )
|
|
return FALSE;
|
|
|
|
// get our cursor position first since the merge prompt
|
|
// may change our current position
|
|
//
|
|
// BUGBUG: what if the use tabbed to this link and then pressed ENTER?
|
|
// We should then anchor the menu to the bottom-left of the parent
|
|
// window. Right?
|
|
POINT pt;
|
|
|
|
GetCursorPos(&pt);
|
|
|
|
HWND hwnd = GetFocus();
|
|
if ( hwnd ) {
|
|
DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE );
|
|
if ( dwStyle & BS_NOTIFY )
|
|
{
|
|
RECT rc;
|
|
if( GetWindowRect(hwnd, &rc) ) {
|
|
pt.y = rc.top+((rc.bottom-rc.top)/2);
|
|
pt.x = rc.left + ((RECT_HEIGHT(rc)/3)*2); // two-thirds of the height of the button
|
|
}
|
|
}
|
|
}
|
|
|
|
// get the parent window
|
|
HWND hWndParent = NULL ;
|
|
if( m_fButton && m_hwndDisplayButton )
|
|
hWndParent = m_hwndDisplayButton;
|
|
else if( m_hwnd && IsWindow(m_hwnd) )
|
|
hWndParent = m_hwnd;
|
|
else
|
|
{
|
|
// Tunnel through IE to get the HWND of HTML Help's frame window.
|
|
hWndParent = GetHtmlHelpFrameWindow() ;
|
|
}
|
|
|
|
// If nothing else works, try the actice window. Eck!
|
|
if( !hWndParent )
|
|
hWndParent = GetActiveWindow();
|
|
|
|
// Worse, try the desktop window!
|
|
if( !hWndParent )
|
|
hWndParent = GetDesktopWindow();
|
|
|
|
//
|
|
// <mc> Find a CExCollection pointer...
|
|
//
|
|
CExCollection* pExCollection = NULL;
|
|
CStr cstr;
|
|
|
|
if ( m_pWebBrowserApp )
|
|
{
|
|
m_pWebBrowserApp->GetLocationURL(&cstr);
|
|
pExCollection = GetCurrentCollection(NULL, (PCSTR)cstr);
|
|
}
|
|
|
|
// should we always display the jump list even on one hit?
|
|
BOOL bAlwaysShowList = FALSE;
|
|
if( m_flags[0] == 1 )
|
|
bAlwaysShowList = TRUE;
|
|
|
|
// call our shared "Topics Found" handler
|
|
|
|
BOOL fPopupMenu = m_fPopupMenu;
|
|
|
|
if (fPopupMenu == TRUE && OSLangMatchesChm(pExCollection) != S_OK)
|
|
fPopupMenu = FALSE;
|
|
|
|
HRESULT hr = OnWordWheelLookup( m_ptblItems, pExCollection, m_pszDefaultTopic, &pt, hWndParent,
|
|
!fPopupMenu, fKLink, bTestMode, TRUE, bAlwaysShowList, TRUE, m_pszWindow);
|
|
|
|
if( FAILED( hr ) )
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
LRESULT CHtmlHelpControl::StaticTextControlSubWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HDC hDC;
|
|
RECT rc;
|
|
|
|
CHtmlHelpControl* pThis = (CHtmlHelpControl*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
switch (msg)
|
|
{
|
|
case WM_KILLFOCUS:
|
|
case WM_SETFOCUS:
|
|
if ( pThis && pThis->m_imgType == IMG_TEXT )
|
|
{
|
|
hDC = ::GetDC(hwnd);
|
|
GetClientRect(hwnd, &rc);
|
|
::DrawFocusRect(hDC, &rc);
|
|
ReleaseDC(hwnd, hDC);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
if ( wParam == VK_RETURN || (wParam == VK_SPACE && (pThis->m_imgType == IMG_TEXT)) )
|
|
{
|
|
PostMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(IDBTN_DISPLAY, BN_CLICKED), (LPARAM)hwnd);
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
if ( pThis )
|
|
return CallWindowProc(pThis->m_lpfnlStaticTextControlWndProc, hwnd, msg, wParam, lParam);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
BOOL CHtmlHelpControl::CreateOnClickButton(void)
|
|
{
|
|
// First create the window, then size it to match text and/or bitmap
|
|
PSTR pszClassName;
|
|
char szWindowText[MAX_PATH];
|
|
|
|
if ( m_imgType == IMG_BUTTON )
|
|
{
|
|
pszClassName = "button";
|
|
WideCharToMultiByte( m_CodePage, 0, m_pwszButtonText, -1, szWindowText, sizeof(szWindowText), NULL, NULL );
|
|
}
|
|
else
|
|
{
|
|
pszClassName = "static";
|
|
*szWindowText = '\0';
|
|
}
|
|
m_hwndDisplayButton = CreateWindowEx(0, pszClassName, szWindowText, WS_CHILD | m_flags[1] | WS_VISIBLE | BS_NOTIFY,
|
|
0, 0, NOTEXT_BTN_WIDTH, NOTEXT_BTN_WIDTH, m_hwnd, (HMENU) IDBTN_DISPLAY,
|
|
_Module.GetModuleInstance(), NULL);
|
|
|
|
if (!m_hwndDisplayButton)
|
|
return FALSE;
|
|
//
|
|
// <mc>
|
|
// I'm subclassing the static text controls only for the purpose of implementing proper
|
|
// focus and UI activation. I have to do this because I don't have any other way to be notified
|
|
// of loss and acquisition of focus. A much better way to do this would be to implement these
|
|
// controls as window-less.
|
|
// </mc>
|
|
//
|
|
// 4/27/98 - <mc> Changed to also subclass buttons so we can have enter key support.
|
|
//
|
|
m_lpfnlStaticTextControlWndProc = (WNDPROC)GetWindowLongPtr(m_hwndDisplayButton, GWLP_WNDPROC);
|
|
SetWindowLongPtr(m_hwndDisplayButton, GWLP_USERDATA, (LONG_PTR)this);
|
|
SetWindowLongPtr(m_hwndDisplayButton, GWLP_WNDPROC, (LONG_PTR)StaticTextControlSubWndProc);
|
|
|
|
if (m_pszBitmap) {
|
|
char szBitmap[MAX_PATH];
|
|
BOOL m_fBuiltInImage = (IsSamePrefix(m_pszBitmap, txtShortcut, -2));
|
|
if (!m_fBuiltInImage) {
|
|
if (!ConvertToCacheFile(m_pszBitmap, szBitmap)) {
|
|
AuthorMsg(IDS_CANT_OPEN, m_pszBitmap);
|
|
|
|
// REVIEW: better default?
|
|
|
|
if (m_fIcon)
|
|
goto NoImage;
|
|
m_hImage = LoadBitmap(_Module.GetResourceInstance(), txtShortcut);
|
|
goto GotImage;
|
|
}
|
|
}
|
|
|
|
if (m_fBuiltInImage)
|
|
m_hImage = LoadBitmap(_Module.GetResourceInstance(), m_pszBitmap);
|
|
else
|
|
m_hImage = LoadImage(_Module.GetResourceInstance(), szBitmap,
|
|
(m_fIcon ? IMAGE_ICON : IMAGE_BITMAP), 0, 0,
|
|
LR_LOADFROMFILE);
|
|
if (!m_hImage) {
|
|
AuthorMsg(IDS_CANT_OPEN, m_pszBitmap);
|
|
// REVIEW: we should use a default bitmap
|
|
goto NoImage;
|
|
}
|
|
|
|
GotImage:
|
|
if (m_fIcon) {
|
|
|
|
// We use IMAGE_ICON for both cursors and icons. Internally,
|
|
// the only significant difference is that a cursor could be
|
|
// forced to monochrome if we used the IMAGE_CURSOR command.
|
|
|
|
if (m_imgType == IMG_BUTTON) {
|
|
// REVIEW: should check actual ICON/CURSOR size
|
|
MoveWindow(m_hwndDisplayButton, 0, 0,
|
|
32 + CXBUTTONEXTRA,
|
|
32 + CYBUTTONEXTRA, FALSE);
|
|
|
|
SendMessage(m_hwndDisplayButton, BM_SETIMAGE,
|
|
IMAGE_ICON, (LPARAM) m_hImage);
|
|
}
|
|
else
|
|
SendMessage(m_hwndDisplayButton, STM_SETIMAGE, IMAGE_CURSOR,
|
|
(LPARAM) m_hImage);
|
|
}
|
|
else {
|
|
if (m_imgType == IMG_BUTTON) {
|
|
BITMAP bmp;
|
|
GetObject(m_hImage, sizeof(bmp), &bmp);
|
|
MoveWindow(m_hwndDisplayButton, 0, 0,
|
|
bmp.bmWidth + CXBUTTONEXTRA,
|
|
bmp.bmHeight + CYBUTTONEXTRA, FALSE);
|
|
|
|
SendMessage(m_hwndDisplayButton, BM_SETIMAGE,
|
|
IMAGE_BITMAP, (LPARAM) m_hImage);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
SendMessage(m_hwndDisplayButton, WM_SETFONT,
|
|
m_hfont ? (WPARAM) m_hfont : (WPARAM) _Resource.GetUIFont(), FALSE);
|
|
|
|
NoImage:
|
|
if ( m_pwszButtonText && *m_pwszButtonText )
|
|
{
|
|
HDC hdc = GetDC(m_hwndDisplayButton);
|
|
|
|
if (hdc == NULL)
|
|
return FALSE;
|
|
|
|
HFONT hfontOld = (HFONT) SelectObject(hdc,
|
|
m_hfont ? m_hfont : _Resource.GetUIFont());
|
|
|
|
SIZE size;
|
|
IntlGetTextExtentPoint32W(hdc, m_pwszButtonText, lstrlenW(m_pwszButtonText), &size);
|
|
|
|
SelectObject(hdc, hfontOld);
|
|
ReleaseDC(m_hwndDisplayButton, hdc);
|
|
|
|
if (m_imgType == IMG_TEXT)
|
|
MoveWindow(m_hwndDisplayButton, 0, 0, size.cx, size.cy, FALSE);
|
|
else
|
|
MoveWindow(m_hwndDisplayButton, 0, 0, size.cx + CXBUTTONEXTRA,
|
|
size.cy + CYBUTTONEXTRA, FALSE);
|
|
}
|
|
GetWindowRect(m_hwndDisplayButton, &m_rcButton);
|
|
|
|
// REVIEW: will this set ALL static windows to use this cursor?
|
|
|
|
// change the cursor to the hand icon
|
|
if (m_imgType == IMG_TEXT) {
|
|
SetClassLongPtr(m_hwndDisplayButton, GCLP_HCURSOR,
|
|
(LONG_PTR) LoadCursor(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDCUR_HAND)));
|
|
}
|
|
|
|
// set the text color -- default to visited link color if not specified
|
|
if( m_imgType == IMG_TEXT ) {
|
|
if( m_clrFont == CLR_INVALID )
|
|
m_clrFont = m_clrFontLink;
|
|
}
|
|
|
|
// enable/disable dynalinks
|
|
if( (m_action == ACT_KLINK || m_action == ACT_ALINK) && m_flags[2] == 1 ) {
|
|
if( !OnAKLink((m_action == ACT_KLINK),TRUE) ) {
|
|
EnableWindow( m_hwndDisplayButton, FALSE ); // disable the window
|
|
if( m_imgType == IMG_TEXT )
|
|
m_clrFont = m_clrFontDisabled;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: GetTextDimensions
|
|
|
|
PURPOSE:
|
|
|
|
PARAMETERS:
|
|
hwnd
|
|
psz
|
|
hfont -- may be NULL
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
If hfont is NULL, font in the window's DC will be used
|
|
|
|
MODIFICATION DATES:
|
|
01-Sep-1997 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
static DWORD GetTextDimensions(HWND hwnd, PCSTR psz, HFONT hfont)
|
|
{
|
|
HDC hdc = GetDC(hwnd);
|
|
|
|
if (hdc == NULL)
|
|
return 0L;
|
|
|
|
HFONT hfontOld;
|
|
if (hfont)
|
|
hfontOld = (HFONT) SelectObject(hdc, hfont);
|
|
SIZE size;
|
|
GetTextExtentPoint(hdc, psz, (int)strlen(psz), &size);
|
|
DWORD dwRet = MAKELONG(size.cx, size.cy) +
|
|
MAKELONG(CXBUTTONEXTRA, CYBUTTONEXTRA);
|
|
if (hfont)
|
|
SelectObject(hdc, hfontOld);
|
|
ReleaseDC(hwnd, hdc);
|
|
return dwRet;
|
|
}
|
|
|
|
// undocumented WinHelp API commands
|
|
|
|
#define HELP_HASH 0x095 // Jump to file and topic based on hash
|
|
#define HELP_HASH_POPUP 0x096 // Put up glossary based on hash
|
|
#define MAX_WINHELP 247
|
|
|
|
void STDCALL CHtmlHelpControl::OnClick(void)
|
|
{
|
|
switch (m_action) {
|
|
case ACT_ABOUT_BOX:
|
|
{
|
|
CAboutBox aboutbox(this);
|
|
aboutbox.DoModal();
|
|
}
|
|
break;
|
|
|
|
case ACT_HHCTRL_VERSION:
|
|
ModalDialog(TRUE);
|
|
MsgBox(IDS_VERSION);
|
|
ModalDialog(FALSE);
|
|
break;
|
|
|
|
case ACT_RELATED_TOPICS:
|
|
{
|
|
if (m_pSiteMap->Count() == 0) // don't allow zero items
|
|
break;
|
|
|
|
CExCollection* pExCollection = NULL;
|
|
CStr cstr;
|
|
|
|
if ( m_pWebBrowserApp )
|
|
{
|
|
m_pWebBrowserApp->GetLocationURL(&cstr);
|
|
pExCollection = GetCurrentCollection(NULL, (PCSTR)cstr);
|
|
}
|
|
|
|
if (m_pSiteMap->Count() == 1 && !m_flags[0])
|
|
{
|
|
SITEMAP_ENTRY *pSiteMapEntry = m_pSiteMap->GetSiteMapEntry(1);
|
|
if(pSiteMapEntry)
|
|
JumpToUrl(pSiteMapEntry, m_pSiteMap);
|
|
}
|
|
else if (m_fPopupMenu && OSLangMatchesChm(pExCollection) == S_OK)
|
|
OnRelatedMenu();
|
|
else
|
|
{
|
|
UINT CodePage = GetCodePage();
|
|
CWTable tblTitles( CodePage );
|
|
TCHAR szURL[INTERNET_MAX_URL_LENGTH];
|
|
for (int i = 0; i < m_pSiteMap->Count(); i++)
|
|
{
|
|
strcpy(szURL, m_pSiteMap->GetSiteMapEntry(i+1)->pszText );
|
|
tblTitles.AddIntAndString(i+1, szURL);
|
|
}
|
|
CTopicList TopicList(this, &tblTitles, m_hfont);
|
|
if (TopicList.DoModal() > 0) {
|
|
int iIndex = tblTitles.GetInt( TopicList.m_pos );
|
|
JumpToUrl(m_pSiteMap->GetSiteMapEntry(iIndex), m_pSiteMap);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ACT_WINHELP:
|
|
if (!m_ptblItems || m_ptblItems->CountStrings() < 1) {
|
|
AuthorMsg(IDS_ACT_WINHELP_NO_HELP);
|
|
break;
|
|
}
|
|
if (m_ptblItems->CountStrings() < 2) {
|
|
AuthorMsg(IDS_ACT_WINHELP_NO_ID);
|
|
break;
|
|
}
|
|
|
|
// Note that we don't track this, and therefore don't force
|
|
// the help file closed.
|
|
{
|
|
PSTR pc = m_ptblItems->GetPointer(1);
|
|
PSTR pcMax = NULL;
|
|
if ( strlen( pc ) > MAX_WINHELP )
|
|
{
|
|
pcMax = new char[MAX_WINHELP];
|
|
strncpy( pcMax, pc, MAX_WINHELP-1 );
|
|
pcMax[MAX_WINHELP-1] = 0;
|
|
}
|
|
else
|
|
pcMax = pc;
|
|
::WinHelp(m_hwnd, pcMax,
|
|
IsDigit(*(m_ptblItems->GetPointer(2))) ?
|
|
(m_fWinHelpPopup ? HELP_CONTEXTPOPUP : HELP_CONTEXT) :
|
|
(m_fWinHelpPopup ? HELP_HASH_POPUP : HELP_HASH),
|
|
IsDigit(*(m_ptblItems->GetPointer(2))) ?
|
|
Atoi(m_ptblItems->GetPointer(2)) :
|
|
WinHelpHashFromSz(m_ptblItems->GetPointer(2)));
|
|
if ( pcMax != pc )
|
|
delete [] pcMax;
|
|
}
|
|
if ( m_fWinHelpPopup )
|
|
g_HackForBug_HtmlHelpDB_1884 = 1;
|
|
break;
|
|
|
|
case ACT_SHORTCUT:
|
|
// don't allow if running in IE
|
|
if( !GetCurrentCollection(NULL, (PCSTR)NULL) ) {
|
|
HWND hWndParent;
|
|
if (!IsValidWindow(m_hwnd)) // in case we are windowless
|
|
hWndParent = FindTopLevelWindow(GetActiveWindow());
|
|
else
|
|
hWndParent = FindTopLevelWindow(GetParent(m_hwnd));
|
|
char szMsg[1024];
|
|
strcpy( szMsg, GetStringResource( IDS_REQUIRES_HTMLHELP ) );
|
|
MessageBox( hWndParent, szMsg, _Resource.MsgBoxTitle(),
|
|
MB_OK | MB_ICONWARNING | MB_SETFOREGROUND );
|
|
break;
|
|
}
|
|
if (m_ptblItems && m_ptblItems->CountStrings()) {
|
|
ShortCut(this, m_ptblItems->GetPointer(1),
|
|
(m_ptblItems->CountStrings() > 1 ?
|
|
m_ptblItems->GetPointer(2) : ""),
|
|
m_hwndParent);
|
|
}
|
|
else
|
|
AuthorMsg(IDS_SHORTCUT_ARGULESS);
|
|
break;
|
|
|
|
case ACT_HHWIN_PRINT:
|
|
case ACT_CLOSE:
|
|
case ACT_MAXIMIZE:
|
|
case ACT_MINIMIZE:
|
|
{
|
|
HWND hwndParent;
|
|
if (!IsValidWindow(m_hwnd)) // in case we are windowless
|
|
hwndParent = FindTopLevelWindow(GetActiveWindow());
|
|
else
|
|
hwndParent = FindTopLevelWindow(GetParent(m_hwnd));
|
|
switch (m_action) {
|
|
case ACT_CLOSE:
|
|
PostMessage(hwndParent, WM_CLOSE, 0, 0);
|
|
return;
|
|
|
|
case ACT_MINIMIZE:
|
|
ShowWindow(hwndParent, SW_MINIMIZE);
|
|
return;
|
|
|
|
case ACT_MAXIMIZE:
|
|
ShowWindow(hwndParent,
|
|
IsZoomed(hwndParent) ? SW_RESTORE : SW_SHOWMAXIMIZED);
|
|
return;
|
|
|
|
case ACT_HHWIN_PRINT:
|
|
{
|
|
char szClass[256];
|
|
GetClassName(hwndParent, szClass, sizeof(szClass));
|
|
if (IsSamePrefix(szClass, txtHtmlHelpWindowClass, -2)) {
|
|
PostMessage(hwndParent, WM_COMMAND, IDTB_PRINT, 0);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ACT_TCARD:
|
|
{
|
|
if (IsEmptyString(m_pszActionData))
|
|
break; // REVIEW: nag the help author
|
|
WPARAM wParam = Atoi(m_pszActionData);
|
|
LPARAM lParam = 0;
|
|
PCSTR psz = StrChr(m_pszActionData, ',');
|
|
if (psz) {
|
|
psz = FirstNonSpace(psz + 1);
|
|
if (IsDigit(*psz))
|
|
lParam = Atoi(psz);
|
|
else
|
|
lParam = (LPARAM) psz;
|
|
}
|
|
HWND hwndParent;
|
|
if (!IsValidWindow(m_hwnd)) // in case we are windowless
|
|
hwndParent = FindTopLevelWindow(GetActiveWindow());
|
|
else
|
|
hwndParent = FindTopLevelWindow(GetParent(m_hwnd));
|
|
if (hwndParent)
|
|
SendMessage(hwndParent, WM_TCARD, wParam, lParam);
|
|
}
|
|
break;
|
|
|
|
case ACT_KLINK:
|
|
OnAKLink(TRUE);
|
|
break;
|
|
|
|
case ACT_ALINK:
|
|
OnAKLink(FALSE);
|
|
break;
|
|
|
|
case ACT_SAMPLE:
|
|
if(!OnCopySample())
|
|
MsgBox(IDS_SAMPLE_ERROR);
|
|
break;
|
|
|
|
default:
|
|
// REVIEW: nag the help author
|
|
break;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: OSLangMatchesChm()
|
|
|
|
PURPOSE: Checks lang of os verses the lang of this title
|
|
|
|
PARAMETERS:
|
|
|
|
RETURNS: S_OK, S_FALSE
|
|
|
|
MODIFICATION DATES:
|
|
11-Nov-1998
|
|
|
|
***************************************************************************/
|
|
HRESULT OSLangMatchesChm(CExCollection *pCollection)
|
|
{
|
|
if (pCollection == NULL)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
CTitleInformation *pInfo = pCollection->GetMasterTitle()->GetInfo();
|
|
LANGID MasterLangId;
|
|
|
|
if (pInfo)
|
|
MasterLangId = LANGIDFROMLCID(pInfo->GetLanguage());
|
|
else
|
|
return S_FALSE;
|
|
|
|
CLanguage cLang;
|
|
if (cLang.GetUiLanguage() == MasterLangId)
|
|
return S_OK;
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: HashFromSz
|
|
|
|
PURPOSE: Convert a string into a WinHelp hash number
|
|
|
|
PARAMETERS:
|
|
pszKey -- string to convert
|
|
|
|
RETURNS: WinHelp-compatible hash number
|
|
|
|
COMMENTS:
|
|
This is the same algorithm that WinHelp and Help Workshop uses. The
|
|
result can be used to jump to a topic in a help file.
|
|
|
|
MODIFICATION DATES:
|
|
14-Jun-1997 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
static const HASH MAX_CHARS = 43L;
|
|
|
|
extern "C" HASH WinHelpHashFromSz(PCSTR pszKey)
|
|
{
|
|
HASH hash = 0;
|
|
|
|
int cch = (int)strlen(pszKey);
|
|
|
|
// REVIEW: 14-Oct-1993 [ralphw] -- Note lack of check for a hash collision.
|
|
|
|
for (int ich = 0; ich < cch; ++ich) {
|
|
if (pszKey[ich] == '!')
|
|
hash = (hash * MAX_CHARS) + 11;
|
|
else if (pszKey[ich] == '.')
|
|
hash = (hash * MAX_CHARS) + 12;
|
|
else if (pszKey[ich] == '_')
|
|
hash = (hash * MAX_CHARS) + 13;
|
|
else if (pszKey[ich] == '0')
|
|
hash = (hash * MAX_CHARS) + 10;
|
|
|
|
else if (pszKey[ich] <= 'Z')
|
|
hash = (hash * MAX_CHARS) + (pszKey[ich] - '0');
|
|
else
|
|
hash = (hash * MAX_CHARS) + (pszKey[ich] - '0' - ('a' - 'A'));
|
|
}
|
|
|
|
/*
|
|
* Since the value 0 is reserved as a nil value, if any topic id
|
|
* actually hashes to this value, we just move it.
|
|
*/
|
|
|
|
return (hash == 0 ? 0 + 1 : hash);
|
|
}
|
|
|
|
VOID (WINAPI* pSHHelpShortcuts_RunDLL)(HWND hwndStub, HINSTANCE hAppInstance, LPCSTR lpszCmdLine, int nCmdShow);
|
|
static const char txtShellShortCut[] = "shell32.dll,SHHelpShortcuts_RunDLL";
|
|
static const char txtShell32Dll[] = "shell32.dll";
|
|
|
|
BOOL ShortCut(CHtmlHelpControl* phhctrl, LPCSTR pszString1, LPCSTR pszString2,
|
|
HWND hwndMsgOwner)
|
|
{
|
|
HWND hwndApp;
|
|
HINSTANCE hinstRet;
|
|
CHourGlass hourglass;
|
|
|
|
// Make a copy so we can modify it
|
|
|
|
if (IsEmptyString(pszString1))
|
|
return FALSE;
|
|
|
|
CStr csz(pszString1);
|
|
PSTR pszComma = StrChr(csz.psz, ',');
|
|
if (!pszComma) {
|
|
AuthorMsg(IDS_INVALID_SHORTCUT_ITEM1, pszString1, hwndMsgOwner, phhctrl);
|
|
return FALSE;
|
|
}
|
|
*pszComma = '\0';
|
|
RemoveTrailingSpaces(csz.psz);
|
|
PCSTR pszClass = csz.psz;
|
|
PSTR pszApplication = FirstNonSpace(pszComma + 1);
|
|
pszComma = StrChr(pszApplication, ',');
|
|
if (pszComma)
|
|
*pszComma = '\0';
|
|
RemoveTrailingSpaces(pszApplication);
|
|
PSTR pszParams = "";
|
|
if (pszComma) {
|
|
pszParams = FirstNonSpace(pszComma + 1);
|
|
RemoveTrailingSpaces(pszParams);
|
|
}
|
|
|
|
PSTR pszUrl;
|
|
UINT msg;
|
|
WPARAM wParam;
|
|
LPARAM lParam;
|
|
if (!IsEmptyString(pszString2)) {
|
|
CStr cszArg;
|
|
pszUrl = cszArg.GetArg(pszString2, TRUE);
|
|
msg = Atoi(cszArg.psz);
|
|
pszUrl = cszArg.GetArg(pszUrl, TRUE);
|
|
wParam = Atoi(cszArg.psz);
|
|
pszUrl = cszArg.GetArg(pszUrl, TRUE);
|
|
lParam = Atoi(cszArg.psz);
|
|
pszUrl = FirstNonSpace(pszUrl);
|
|
}
|
|
else {
|
|
pszUrl = (PSTR) txtZeroLength;
|
|
msg = 0;
|
|
}
|
|
|
|
CStr strUrl;
|
|
|
|
ASSERT(phhctrl->m_pWebBrowserApp != NULL);
|
|
phhctrl->m_pWebBrowserApp->GetLocationURL(&strUrl);
|
|
|
|
// check for NULL pointer
|
|
//
|
|
if(strUrl.IsEmpty())
|
|
goto Fail;
|
|
|
|
// Execute the shortcut only if we're in a local CHM file.
|
|
//
|
|
if(strstr(strUrl, "\\\\") || strstr(strUrl, "//"))
|
|
goto Fail;
|
|
|
|
if (IsEmptyString(pszClass) || !(hwndApp = FindWindow(pszClass, NULL))) {
|
|
|
|
// 27-Sep-1997 [ralphw] We special-case shell32.dll,SHHelpShortcuts_RunDLL"
|
|
// in order to run the shortcut without having to load rundll32.
|
|
|
|
if (IsSamePrefix(pszParams, txtShellShortCut)) {
|
|
if (!pSHHelpShortcuts_RunDLL) {
|
|
HINSTANCE hmod = LoadLibrary(txtShell32Dll);
|
|
if (hmod) {
|
|
(FARPROC&) pSHHelpShortcuts_RunDLL = GetProcAddress(hmod,
|
|
"SHHelpShortcuts_RunDLL");
|
|
}
|
|
}
|
|
if (pSHHelpShortcuts_RunDLL) {
|
|
pSHHelpShortcuts_RunDLL(NULL, _Module.GetModuleInstance(),
|
|
FirstNonSpace(pszParams + sizeof(txtShellShortCut)),
|
|
SW_SHOW);
|
|
return TRUE;
|
|
}
|
|
}
|
|
hinstRet = ShellExecute(hwndMsgOwner,
|
|
((strstr(pszApplication, ".cpl") || strstr(pszApplication, ".CPL")) ? txtCplOpen : txtOpen),
|
|
pszApplication, pszParams, "", SW_SHOW);
|
|
if ( hinstRet != (HANDLE)-1 && hinstRet <= (HANDLE)32) {
|
|
AuthorMsg(IDS_CANNOT_RUN, pszApplication, hwndMsgOwner, phhctrl);
|
|
Fail:
|
|
if (phhctrl->m_pszWindow) {
|
|
if (IsCompiledHtmlFile(phhctrl->m_pszWindow, NULL))
|
|
OnDisplayTopic(hwndMsgOwner, phhctrl->m_pszWindow, 0);
|
|
else {
|
|
CWStr cwJump(phhctrl->m_pszWindow);
|
|
HlinkSimpleNavigateToString(cwJump, NULL,
|
|
NULL, phhctrl->GetIUnknown(), NULL, NULL, 0, NULL);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
if (IsIconic(hwndApp))
|
|
ShowWindow(hwndApp, SW_RESTORE); // Must restore minimized app
|
|
SetForegroundWindow(hwndApp);
|
|
}
|
|
|
|
if (msg > 0) {
|
|
int i;
|
|
|
|
if (!hwndApp) {
|
|
|
|
// Wait for up to 7 seconds for the process to initialize
|
|
|
|
for (i = 0; i < 70; i++) {
|
|
if ((hwndApp = FindWindow(pszClass, NULL)))
|
|
break;
|
|
Sleep(100);
|
|
}
|
|
}
|
|
if (!hwndApp) {
|
|
|
|
// Probably means the window class has changed.
|
|
|
|
AuthorMsg(IDS_CLASS_NOT_FOUND, pszClass, hwndMsgOwner, phhctrl);
|
|
|
|
if (phhctrl->m_pszWindow) {
|
|
if (IsCompiledHtmlFile(phhctrl->m_pszWindow, NULL))
|
|
OnDisplayTopic(hwndMsgOwner, phhctrl->m_pszWindow, 0);
|
|
else {
|
|
CWStr cwJump(phhctrl->m_pszWindow);
|
|
HlinkSimpleNavigateToString(cwJump, NULL,
|
|
NULL, phhctrl->GetIUnknown(), NULL, NULL, 0, NULL);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
SetForegroundWindow(hwndApp);
|
|
|
|
if (msg)
|
|
PostMessage(hwndApp, msg, wParam, lParam);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL CAboutBox::OnBeginOrEnd(void)
|
|
{
|
|
if (m_fInitializing) {
|
|
|
|
if (m_phhCtrl && m_phhCtrl->m_ptblItems && m_phhCtrl->m_ptblItems->CountStrings())
|
|
SetWindowText(m_phhCtrl->m_ptblItems->GetPointer(1));
|
|
|
|
for (int id = IDC_LINE1; id <= IDC_LINE3; id++) {
|
|
|
|
// -2 because CTable is 1-based, and we skip over the title
|
|
|
|
if (m_phhCtrl->m_ptblItems == NULL)
|
|
break;
|
|
|
|
if (id - IDC_LINE1 > m_phhCtrl->m_ptblItems->CountStrings() - 2)
|
|
break;
|
|
|
|
SetWindowText(id,
|
|
m_phhCtrl->m_ptblItems->GetPointer((id - IDC_LINE1) + 2));
|
|
}
|
|
|
|
// Hide any unused controls
|
|
|
|
while (id <= IDC_LINE3)
|
|
HideWindow(id++);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void CHtmlHelpControl::OnDrawStaticText(DRAWITEMSTRUCT* pdis)
|
|
{
|
|
if (!m_pwszButtonText || !*m_pwszButtonText )
|
|
return;
|
|
|
|
// REVIEW: since we are the only ones drawing into this DC, do we really
|
|
// need to restore the previous background mode and foreground text color?
|
|
|
|
int iBack = SetBkMode(pdis->hDC, TRANSPARENT);
|
|
COLORREF clrLast = CLR_INVALID;
|
|
if (m_clrFont != CLR_INVALID)
|
|
clrLast = SetTextColor(pdis->hDC, m_clrFont);
|
|
RECT rc;
|
|
|
|
GetClientRect(pdis->hwndItem, &rc);
|
|
IntlExtTextOutW(pdis->hDC, rc.left, rc.top, ETO_RTLREADING, &rc, m_pwszButtonText, lstrlenW(m_pwszButtonText), NULL);
|
|
|
|
// DrawTextEx(pdis->hDC, (PSTR) m_pszButtonText, -1, &rc, DT_BOTTOM | DT_LEFT | DT_NOCLIP | DT_SINGLELINE | DT_NOPREFIX | DT_RTLREADING, NULL);
|
|
|
|
if ( pdis->hwndItem == ::GetFocus() )
|
|
::DrawFocusRect(pdis->hDC, &rc);
|
|
SetBkMode(pdis->hDC, iBack);
|
|
if (clrLast != CLR_INVALID)
|
|
SetTextColor(pdis->hDC, clrLast);
|
|
}
|
|
|
|
void CHtmlHelpControl::OnRelatedMenu()
|
|
{
|
|
POINT pt;
|
|
|
|
GetCursorPos(&pt);
|
|
|
|
#if 1 // reverted bug fix #5516
|
|
HWND hwnd=GetFocus();
|
|
if ( hwnd )
|
|
{
|
|
DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE );
|
|
if ( dwStyle & BS_NOTIFY )
|
|
{
|
|
|
|
RECT rc;
|
|
if( GetWindowRect(hwnd, &rc) ) {
|
|
pt.y = rc.top+((rc.bottom-rc.top)/2);
|
|
pt.x = rc.left + ((RECT_HEIGHT(rc)/3)*2); // two-thirds of the height of the button
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
HMENU hMenu = CreatePopupMenu();
|
|
if (!hMenu)
|
|
return; // BUGBUG: nag the help author
|
|
|
|
for (int i = 1; i <= m_pSiteMap->Count(); i++) {
|
|
HxAppendMenu(hMenu, MF_STRING, i,
|
|
m_pSiteMap->GetSiteMapEntry(i)->pszText);
|
|
}
|
|
|
|
int iIndex = TrackPopupMenu(hMenu,
|
|
TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD,
|
|
pt.x, pt.y, 0, (m_hwnd == NULL ? hwnd : m_hwnd), NULL);
|
|
|
|
if (iIndex)
|
|
JumpToUrl(m_pSiteMap->GetSiteMapEntry(iIndex), m_pSiteMap);
|
|
|
|
DestroyMenu(hMenu);
|
|
}
|
|
|
|
void CHtmlHelpControl::OnRelatedCommand(int idCommand)
|
|
{
|
|
if (idCommand <= IDM_RELATED_TOPIC)
|
|
return;
|
|
|
|
if (m_action != ACT_RELATED_TOPICS) {
|
|
if (!m_ptblTitles)
|
|
return;
|
|
|
|
char szURL[INTERNET_MAX_URL_LENGTH];
|
|
|
|
int iIndex = m_ptblTitles->GetInt(idCommand - IDM_RELATED_TOPIC);
|
|
m_ptblURLs->GetString( szURL, iIndex );
|
|
|
|
if (m_pszWindow) {
|
|
CStr csz(">");
|
|
csz += m_pszWindow;
|
|
OnDisplayTopic(m_hwnd, csz, (DWORD_PTR) szURL);
|
|
return;
|
|
}
|
|
|
|
CWStr cwJump(szURL);
|
|
CWStr cwFrame(m_pszFrame ? m_pszFrame : txtZeroLength);
|
|
HlinkSimpleNavigateToString(cwJump, NULL,
|
|
cwFrame, GetIUnknown(), NULL, NULL, 0, NULL);
|
|
|
|
delete m_ptblTitles;
|
|
m_ptblTitles = NULL;
|
|
delete m_ptblURLs;
|
|
m_ptblURLs = NULL;
|
|
delete m_ptblLocations;
|
|
m_ptblLocations = NULL;
|
|
return;
|
|
}
|
|
|
|
if (idCommand >= ID_VIEW_ENTRY) {
|
|
DisplayAuthorInfo(m_pSiteMap,
|
|
m_pSiteMap->GetSiteMapEntry(idCommand - ID_VIEW_ENTRY));
|
|
return;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
int pos = (idCommand - IDM_RELATED_TOPIC);
|
|
SITEMAP_ENTRY* pSiteMapEntry = m_pSiteMap->GetSiteMapEntry(pos);
|
|
#endif
|
|
|
|
JumpToUrl(m_pSiteMap->GetSiteMapEntry(idCommand - IDM_RELATED_TOPIC),
|
|
m_pSiteMap);
|
|
}
|
|
|
|
void CHtmlHelpControl::OnKeywordSearch(int idCommand)
|
|
{
|
|
CSiteMap* pWebMap;
|
|
|
|
if (IsEmptyString(m_pszWebMap)) {
|
|
// BUGBUG: nag the author
|
|
return;
|
|
}
|
|
else { // use brace to enclose CHourGlass
|
|
|
|
// REVIEW: should we capture the mouse?
|
|
|
|
CHourGlass hourglass;
|
|
|
|
TCHAR szPath[MAX_PATH];
|
|
if (!ConvertToCacheFile(m_pszWebMap, szPath)) {
|
|
CStr cszMsg(IDS_CANT_FIND_FILE, m_pszWebMap);
|
|
MsgBox(cszMsg);
|
|
return;
|
|
}
|
|
|
|
if (m_pindex && isSameString(szPath, m_pindex->GetSiteMapFile()))
|
|
pWebMap = m_pindex;
|
|
else {
|
|
UINT CodePage = 0;
|
|
if( m_pindex && m_pindex->m_phh && m_pindex->m_phh->m_phmData ) {
|
|
CodePage = m_pindex->m_phh->m_phmData->GetInfo()->GetCodePage();
|
|
}
|
|
if (!m_pSiteMap->ReadFromFile(szPath, TRUE, this, CodePage))
|
|
return; // we assume author has already been notified
|
|
pWebMap = m_pSiteMap;
|
|
}
|
|
}
|
|
|
|
int end = m_ptblItems->CountStrings();
|
|
int endWebMap = pWebMap->CountStrings();
|
|
UINT CodePage = m_pSiteMap->GetCodePage();
|
|
CWTable tblTitles( CodePage );
|
|
for (int pos = 1; pos <= end; pos++) {
|
|
PCSTR pszKeyword = m_ptblItems->GetPointer(pos);
|
|
for (int posSite = 1; posSite <= endWebMap; posSite++) {
|
|
SITEMAP_ENTRY* pWebMapEntry = pWebMap->GetSiteMapEntry(posSite);
|
|
if (lstrcmpi(pszKeyword, pWebMapEntry->GetKeyword()) == 0) {
|
|
SITE_ENTRY_URL* pUrl = (SITE_ENTRY_URL*) pWebMapEntry->pUrls;
|
|
for (int url = 0; url < pWebMapEntry->cUrls; url++) {
|
|
tblTitles.AddString(posSite, pWebMapEntry->GetTitle(pUrl));
|
|
pUrl = pWebMap->NextUrlEntry(pUrl);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// we can sort the title table since it contains the index value
|
|
// of the associated URL so just make sure to always fetch the
|
|
// URL index from the selected title string and use that to get the URL
|
|
if( /*bAlphaSortHits*/ TRUE ) {
|
|
tblTitles.SetSorting(GetSystemDefaultLCID());
|
|
tblTitles.SortTable(sizeof(HASH));
|
|
}
|
|
|
|
CTopicList TopicList(this, &tblTitles, m_hfont);
|
|
if (TopicList.DoModal())
|
|
{
|
|
PCSTR pszTitle = tblTitles.GetHashStringPointer(TopicList.m_pos);
|
|
SITEMAP_ENTRY* pWebMapEntry = pWebMap->GetSiteMapEntry(
|
|
tblTitles.GetInt(TopicList.m_pos));
|
|
|
|
// Now find the actual URL that matches the title
|
|
|
|
// BUGBUG: fails with duplicate titles
|
|
|
|
for (int url = 0; url < pWebMapEntry->cUrls; url++) {
|
|
if (strcmp(pszTitle, pWebMap->GetUrlTitle(pWebMapEntry, url)) == 0)
|
|
break;
|
|
}
|
|
ASSERT(url < pWebMapEntry->cUrls);
|
|
JumpToUrl(pWebMapEntry, pWebMap, pWebMap->GetUrlEntry(pWebMapEntry, url));
|
|
}
|
|
}
|
|
|
|
// for bug #3681 -- don't use this new function for any other reason under any circumstances.
|
|
HWND OnDisplayTopicWithRMS(HWND hwndCaller, LPCSTR pszFile, DWORD_PTR dwData)
|
|
{
|
|
BOOL bCollection = IsCollectionFile(pszFile);
|
|
CExTitle* pTitle = NULL;
|
|
CExCollection* pCollection = NULL;
|
|
BOOL bCompiled;
|
|
if( bCollection )
|
|
bCompiled = IsCompiledURL( (PCSTR) dwData );
|
|
else
|
|
bCompiled = IsCompiledURL( pszFile );
|
|
|
|
if( bCompiled ) {
|
|
CExCollection* pCollection = GetCurrentCollection(NULL, pszFile);
|
|
if( pCollection )
|
|
if( (PCSTR) dwData )
|
|
HRESULT hr = pCollection->URL2ExTitle( (PCSTR) dwData, &pTitle );
|
|
}
|
|
|
|
if( pCollection && bCompiled && (!pTitle || FAILED( EnsureStorageAvailability( pTitle, HHRMS_TYPE_TITLE, TRUE, TRUE, FALSE )) ) ) {
|
|
g_LastError.Set(HH_E_FILENOTFOUND) ;
|
|
return NULL;
|
|
}
|
|
|
|
return OnDisplayTopic(hwndCaller, pszFile, dwData );
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// OnKeywordSearch - Handles HH_KEYWORD_LOOKUP Command
|
|
//
|
|
HWND OnKeywordSearch(HWND hwndCaller, PCSTR pszFile, HH_AKLINK* pakLink, BOOL fKLink)
|
|
{
|
|
CStr cszKeywords; // Use so StrToken can modify them. Declared here so JumpNotFound can use.
|
|
CStr cszCompressed;
|
|
BOOL bCollection = IsCollectionFile(pszFile);
|
|
|
|
// We need the following in a bunch of locations in the code below. However, I have no
|
|
// idea which of the following functions may cause side affects which affect this line.
|
|
// Therefore, I can't with any assurance improve the performace of this code.
|
|
// CHHWinType* phh = FindCurProccesWindow(idProcess);
|
|
|
|
if (bCollection || IsCompiledHtmlFile(pszFile, &cszCompressed))
|
|
{
|
|
if (bCollection)
|
|
cszCompressed = pszFile;
|
|
|
|
CHmData* phmData = FindCurFileData(cszCompressed);
|
|
|
|
if (!phmData)
|
|
goto JumpNotFound;
|
|
|
|
UINT CodePage = phmData->m_pTitleCollection->GetMasterTitle()->GetInfo()->GetCodePage();
|
|
|
|
CTable tblItems;
|
|
CWTable tblTitles( CodePage );
|
|
CWTable tblLocations( CodePage );
|
|
CTable tblURLs;
|
|
|
|
if (IsEmptyString(pakLink->pszKeywords))
|
|
goto JumpNotFound;
|
|
if (pakLink->fReserved)
|
|
cszKeywords = (WCHAR*) pakLink->pszKeywords;
|
|
else
|
|
cszKeywords = pakLink->pszKeywords;
|
|
|
|
tblItems.AddString( "" ); // set item1 to NULL -- no external titles
|
|
tblItems.AddString( cszKeywords );
|
|
|
|
GetWordWheelHits( phmData->m_pTitleCollection, &tblItems,
|
|
&tblURLs, &tblTitles, &tblLocations,
|
|
fKLink, FALSE, FALSE );
|
|
|
|
if (tblURLs.CountStrings() < 1)
|
|
{
|
|
// No links found.
|
|
goto JumpNotFound ;
|
|
}
|
|
|
|
// if only one topic then jump to it
|
|
if( tblURLs.CountStrings() == 1 )
|
|
{
|
|
char szURL[INTERNET_MAX_URL_LENGTH];
|
|
|
|
tblURLs.GetString( szURL, 1 );
|
|
|
|
//TODO: This code is repeated below. Share.
|
|
if (pakLink->pszWindow)
|
|
{
|
|
strcat(szURL, ">");
|
|
strcat(szURL, (*pakLink->pszWindow == '>' ?
|
|
pakLink->pszWindow + 1 : pakLink->pszWindow));
|
|
}
|
|
else if (bCollection && phmData->GetDefaultWindow()) // Use the default window for the collection. HH 3428
|
|
{
|
|
strcat(szURL, ">");
|
|
strcat(szURL, (*phmData->GetDefaultWindow() == '>' ?
|
|
phmData->GetDefaultWindow() + 1 : phmData->GetDefaultWindow()));
|
|
|
|
}
|
|
else //REVIEW: Can we find other possible default windows?
|
|
{
|
|
// Pick a random window type. Its unlikely that this is the one you really want, since you get the first one for this process.
|
|
CHHWinType* phh = FindCurWindow();
|
|
if (phh)
|
|
{
|
|
strcat(szURL, ">");
|
|
strcat(szURL, phh->pszType);
|
|
}
|
|
}
|
|
if( bCollection )
|
|
return OnDisplayTopicWithRMS(hwndCaller, pszFile, (DWORD_PTR) szURL);
|
|
return OnDisplayTopicWithRMS(hwndCaller, szURL, 0);
|
|
}
|
|
|
|
// Determine the dialogs parent.
|
|
HWND hwndDlgParent = hwndCaller ;
|
|
CHHWinType* phh = FindCurWindow();
|
|
if (phh)
|
|
{
|
|
// If there is an existing help window, use it for the dialog parent instead of
|
|
// the hwnd passed from the caller. The reason is that the disambiguator will not be
|
|
// modal with the help window, but with the calling app.
|
|
HWND hwnd = phh->GetHwnd();
|
|
if (hwnd && ::IsValidWindow(hwnd))
|
|
{
|
|
hwndDlgParent = hwnd ;
|
|
if (hwnd != GetForegroundWindow())
|
|
{
|
|
BOOL b = SetForegroundWindow(hwnd) ;
|
|
ASSERT(b) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// we can sort the title table since it contains the index value
|
|
// of the associated URL so just make sure to always fetch the
|
|
// URL index from the selected title string and use that to get the URL
|
|
if( /*bAlphaSortHits*/ TRUE ) {
|
|
tblTitles.SetSorting(GetSystemDefaultLCID());
|
|
tblTitles.SortTable(sizeof(HASH));
|
|
}
|
|
|
|
// Display a dialog containing the links to the user.
|
|
CTopicList TopicList(hwndDlgParent, &tblTitles, phmData->GetContentFont(), &tblLocations);
|
|
if (TopicList.DoModal())
|
|
{
|
|
char szURL[INTERNET_MAX_URL_LENGTH];
|
|
int iIndex = tblTitles.GetInt(TopicList.m_pos);
|
|
tblURLs.GetString( szURL, iIndex );
|
|
|
|
//TODO: This code is repeated above. Share.
|
|
if (pakLink->pszWindow)
|
|
{
|
|
strcat(szURL, ">");
|
|
strcat(szURL, (*pakLink->pszWindow == '>' ?
|
|
pakLink->pszWindow + 1 : pakLink->pszWindow));
|
|
}
|
|
else if (bCollection && phmData->GetDefaultWindow()) // Use the default window for the collection. HH 3428
|
|
{
|
|
strcat(szURL, ">");
|
|
strcat(szURL, (*phmData->GetDefaultWindow() == '>' ?
|
|
phmData->GetDefaultWindow() + 1 : phmData->GetDefaultWindow()));
|
|
|
|
}
|
|
else
|
|
{
|
|
// Pick a random window type. Its unlikely that this is the one you really want, since you get the first one for this process.
|
|
CHHWinType* phh = FindCurWindow();
|
|
if (phh)
|
|
{
|
|
strcat(szURL, ">");
|
|
strcat(szURL, phh->pszType);
|
|
}
|
|
}
|
|
if( bCollection )
|
|
return OnDisplayTopicWithRMS(hwndCaller, pszFile, (DWORD_PTR) szURL);
|
|
return OnDisplayTopicWithRMS(hwndCaller, szURL, 0);
|
|
|
|
}
|
|
else //REVIEW: Can we find other possible default windows?
|
|
{
|
|
// Signal that we succeeded, but didn't do anything.
|
|
g_LastError.Set(S_FALSE) ;
|
|
|
|
// User canceled dialog box. Don't go to JumpNotFound.
|
|
return NULL ;
|
|
}
|
|
}
|
|
|
|
// Error handling.
|
|
JumpNotFound:
|
|
if (pakLink->fIndexOnFail) // Display index if the keyword is not found.
|
|
{
|
|
// We only seed the edit control with the characters to the ';'.
|
|
// The StrToken command happens to repalce the ';' with a \0.
|
|
// So we can use cszKeywords, directly as the first keyword.
|
|
// Also, note that cszKeywords.psz is set to NULL during initialization.
|
|
return doDisplayIndex(hwndCaller, pszFile, cszKeywords);
|
|
}
|
|
else if (pakLink->pszUrl) // Display the page pointered to by pszUrl when keyword is not found.
|
|
{
|
|
if (pakLink->pszWindow)
|
|
{
|
|
cszCompressed += ">";
|
|
cszCompressed += (*pakLink->pszWindow == '>' ?
|
|
pakLink->pszWindow + 1 : pakLink->pszWindow);
|
|
}
|
|
else
|
|
{
|
|
CHHWinType* phh = FindCurWindow();
|
|
if (phh)
|
|
{
|
|
cszCompressed += ">";
|
|
cszCompressed += phh->pszType;
|
|
}
|
|
}
|
|
return OnDisplayTopicWithRMS(hwndCaller, cszCompressed, (DWORD_PTR) pakLink->pszUrl);
|
|
}
|
|
else if (pakLink->pszMsgText) // Display a message box with the callers messages.
|
|
{
|
|
MessageBox(hwndCaller, pakLink->pszMsgText,
|
|
IsEmptyString(pakLink->pszMsgTitle) ? "" : pakLink->pszMsgTitle,
|
|
MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// OnSample - Handles HH_COPY_SAMPLE Command
|
|
//
|
|
BOOL CHtmlHelpControl::OnCopySample()
|
|
{
|
|
if (!m_ptblItems)
|
|
return FALSE;
|
|
|
|
// Get the SFL name from "item2"
|
|
//
|
|
CStr cszSFLFileName;
|
|
if (!IsEmptyString(m_ptblItems->GetPointer(2)))
|
|
cszSFLFileName = m_ptblItems->GetPointer(2);
|
|
else
|
|
return FALSE;
|
|
|
|
// Compute the SFL base name
|
|
//
|
|
char szSFLBaseName[_MAX_FNAME];
|
|
strncpy(szSFLBaseName,cszSFLFileName, _MAX_FNAME-1);
|
|
szSFLBaseName[_MAX_FNAME-1] = NULL;
|
|
|
|
cszSFLFileName = szSFLBaseName;
|
|
cszSFLFileName+=".SFL";
|
|
|
|
// Get the dialog title from "item1"
|
|
//
|
|
CStr cszDialogTitle;
|
|
if (!IsEmptyString(m_ptblItems->GetPointer(1)))
|
|
cszDialogTitle = m_ptblItems->GetPointer(1);
|
|
else
|
|
cszDialogTitle = "Sample Application";
|
|
|
|
// Get the current URL
|
|
//
|
|
CStr cszCurUrl;
|
|
if (m_pWebBrowserApp != NULL)
|
|
m_pWebBrowserApp->GetLocationURL(&cszCurUrl);
|
|
else
|
|
return FALSE;
|
|
|
|
char szSflRelativeUrl[INTERNET_MAX_URL_LENGTH];
|
|
char szSflUrl[INTERNET_MAX_URL_LENGTH];
|
|
DWORD dwLength = sizeof(szSflUrl);
|
|
|
|
// Create URL to SFL file
|
|
//
|
|
wsprintf(szSflRelativeUrl,"/samples/%s/%s",szSFLBaseName,cszSFLFileName);
|
|
|
|
strcpy(szSflUrl,cszCurUrl);
|
|
|
|
char *pszTemp = szSflUrl;
|
|
|
|
// Locate the :: in the URL
|
|
//
|
|
while(*pszTemp && !(*pszTemp == ':' && *(pszTemp+1) == ':'))
|
|
++pszTemp;
|
|
|
|
// return if no :: was found
|
|
//
|
|
if(!*pszTemp)
|
|
return FALSE;
|
|
|
|
pszTemp+=2;
|
|
|
|
// place a null after ::
|
|
//
|
|
*pszTemp = 0;
|
|
|
|
strcat(szSflUrl,szSflRelativeUrl);
|
|
|
|
// download the SFL file
|
|
//
|
|
char szPath[MAX_PATH],szSFLFilePath[MAX_PATH];
|
|
|
|
GetTempPath(sizeof(szPath),szPath);
|
|
GetTempFileName(szPath,"SFL",0, szSFLFilePath);
|
|
|
|
HRESULT hr = URLDownloadToFile(NULL, szSflUrl, szSFLFilePath, 0, NULL);
|
|
|
|
if (FAILED(hr))
|
|
DeleteFile(szSFLFilePath);
|
|
|
|
if(!FAILED(hr))
|
|
{
|
|
// Process compressed sample
|
|
//
|
|
// Create URL to SFL file
|
|
//
|
|
|
|
// Null after the :: again in szSflUrl
|
|
//
|
|
*pszTemp = 0;
|
|
|
|
char szSampleBaseUrl[INTERNET_MAX_URL_LENGTH];
|
|
dwLength = sizeof(szSampleBaseUrl);
|
|
wsprintf(szSampleBaseUrl,"%s/samples/%s/",szSflUrl,szSFLBaseName);
|
|
|
|
// call sample UI
|
|
//
|
|
return ProcessSample(szSFLFilePath, szSampleBaseUrl, cszDialogTitle, this, TRUE);
|
|
|
|
// delete the copy of the SFL file
|
|
|
|
DeleteFile(szSFLFilePath);
|
|
}
|
|
else
|
|
{
|
|
CStr cstr;
|
|
PSTR psz = NULL;
|
|
if ( m_pWebBrowserApp )
|
|
{
|
|
m_pWebBrowserApp->GetLocationURL(&cstr);
|
|
psz = (PSTR)cstr;
|
|
}
|
|
// Process uncompressed sample
|
|
|
|
CExCollection *pCurCollection = GetCurrentCollection(NULL, (LPCSTR)psz);
|
|
|
|
if(!pCurCollection)
|
|
return FALSE;
|
|
|
|
CExTitle *pExTitle = NULL;
|
|
|
|
// Get the CExTitle object associated to this URL
|
|
//
|
|
pCurCollection->URL2ExTitle(cszCurUrl, &pExTitle);
|
|
|
|
if(!pExTitle)
|
|
return FALSE;
|
|
|
|
// Make sure the title is open
|
|
// BUGBUG: what do we do if this fails?
|
|
pExTitle->Init();
|
|
|
|
TCHAR *pszSampleLocation = NULL;
|
|
|
|
// Make sure the CTitle object is initialized
|
|
//
|
|
if(pExTitle->m_pTitle)
|
|
{
|
|
// Get the sample location from the CTitle object
|
|
//
|
|
if (pExTitle->GetUsedLocation())
|
|
pszSampleLocation = pExTitle->GetUsedLocation()->SampleLocation;
|
|
}
|
|
|
|
if(!pszSampleLocation)
|
|
{
|
|
// get sample location from CCollection if get from CTitle failed
|
|
pszSampleLocation = pCurCollection->m_Collection.GetSampleLocation();
|
|
}
|
|
|
|
// Make sure we got a sample location
|
|
//
|
|
if(!pszSampleLocation)
|
|
return FALSE;
|
|
|
|
CLocation *pLocation = pCurCollection->m_Collection.FindLocation(pszSampleLocation);
|
|
|
|
if(!pLocation)
|
|
return FALSE;
|
|
|
|
char szSampleBasePath[MAX_PATH],szSFLFilePath[MAX_PATH];
|
|
|
|
// construct full path to uncompressed sample directory
|
|
//
|
|
strcpy(szSampleBasePath,pLocation->GetPath());
|
|
// CatPath(szSampleBasePath,szSFLBaseName);
|
|
// strcat(szSampleBasePath,"\\");
|
|
|
|
// construct full path to SFL
|
|
//
|
|
strcpy(szSFLFilePath,szSampleBasePath);
|
|
CatPath(szSFLFilePath,"\\SFL\\");
|
|
CatPath(szSFLFilePath,cszSFLFileName);
|
|
|
|
// make sure the sample (CD) is available
|
|
if( pExTitle ) {
|
|
pExTitle->SetCurrentAttachmentName( szSFLFilePath );
|
|
if( FAILED(hr = EnsureStorageAvailability( pExTitle, HHRMS_TYPE_ATTACHMENT ) ) ) {
|
|
if( hr == HHRMS_E_SKIP || hr == HHRMS_E_SKIP_ALWAYS )
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// if the user updated the CD location then we need to update the SFL file location
|
|
// before we process the sample
|
|
if( hr == HHRMS_S_LOCATION_UPDATE ) {
|
|
strcpy(szSampleBasePath,pLocation->GetPath());
|
|
strcpy(szSFLFilePath,szSampleBasePath);
|
|
CatPath(szSFLFilePath,"\\SFL\\");
|
|
CatPath(szSFLFilePath,cszSFLFileName);
|
|
}
|
|
|
|
// call sample UI
|
|
//
|
|
return ProcessSample(szSFLFilePath, szSampleBasePath, cszDialogTitle, this, FALSE);
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|