windows-nt/Source/XPSP1/NT/enduser/stuff/hhctrl/hhctrl.cpp

3010 lines
96 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
// Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved.
#include "header.h"
#include "sitemap.h"
#include "hhctrl.h"
#include "LocalObj.h"
#include "Resource.h"
#include "strtable.h"
#include "hha_strtable.h"
#include "infowiz.h"
#include "web.h"
#include "cprint.h"
#include <exdisp.h>
#undef WINUSERAPI
#define WINUSERAPI
#include "htmlhelp.h"
#include <stdio.h>
#ifndef _DEBUG
#undef THIS_FILE
static const char THIS_FILE[] = __FILE__;
#endif
const DWORD STREAMHDR_MAGIC = 12678L;
// DO NOT LOCALIZE THESE!
static const char txtAboutBox[] = "AboutBox";
static const char txtHhCtrlVersion[] = "HH Version";
static const char txtSplash[] = "Splash";
static const char txtTCard[] = "TCard";
static const char txtWinHelp[] = "WinHelp";
static const char txtRelatedTopics[] = "Related Topics";
static const char txtKeywordSearch[] = "Keyword Search";
static const char txtContents[] = "Contents";
static const char txtHelpContents[] = "HelpContents";
static const char txtShortcut[] = "Shortcut";
static const char txtClose[] = "Close";
static const char txtHHWinPrint[] = "HHWinPrint";
static const char txtMinimize[] = "Minimize";
static const char txtMaximize[] = "Maximize";
static const char txtIndex[] = "Index";
static const char txtItem[] = "Item%u";
static const char txtBitmap[] = "Bitmap:";
static const char txtIcon[] = "Icon:";
static const char txtText[] = "Text:";
static const char txtHPad[] = "HPAD=";
static const char txtVPad[] = "VPAD=";
static const char txtHHWin[] = "hhwin:";
static const char txtFileUrl[] = "file:";
static const char txtActKLink[] = "KLink";
static const char txtActSample[] = "Sample";
static const char txtActALink[] = "ALink";
static const char txtMenu[] = "MENU";
static const char txtPopup[] = "Popup";
static const WCHAR txtwImage[] = L"Image";
static const WCHAR txtwFrame[] = L"Frame";
static const WCHAR txtwWindow[] = L"Window";
static const WCHAR txtwFont[] = L"Font";
static const WCHAR txtwFlags[] = L"Flags";
static const WCHAR txtwWebMap[] = L"WebMap";
static const WCHAR txtwCommand[] = L"Command";
static const WCHAR txtwButton[] = L"Button";
static const WCHAR txtwText[] = L"Text";
static const WCHAR txtwDefaultTopic[] = L"DefaultTopic";
// shanemc 7883 - Support bgcolor for index
static const WCHAR txtwBgColor[] = L"BgColor";
// utility functions -- move to util.cpp someday
/***************************************************************************
FUNCTION: FindMessageParent
PURPOSE: Find the parent to send messages to
PARAMETERS:
hwndChild
RETURNS:
COMMENTS:
A control may be a child of a Tab Control, in which case, we
need to send any messages to the Tab Control's parent
MODIFICATION DATES:
20-Mar-1997 [ralphw]
***************************************************************************/
HWND FindMessageParent(HWND hwndChild)
{
HWND hwndParent = GetParent(hwndChild);
char szClass[50];
GetClassName(hwndParent, szClass, sizeof(szClass));
if (IsSamePrefix(szClass, WC_TABCONTROL, -2))
hwndParent = GetParent(hwndParent);
return hwndParent;
}
/***************************************************************************
FUNCTION: JumpToUrl
PURPOSE: Jump to a URL specified in a sitemapentry or SITE_ENTRY_RUL
PARAMETERS:
pUnkOuter -- used for HlinkSimpleNavigateToString
hwndParent -- parent for secondary window (if jumped to)
pSiteMapEntry
pSiteMap
pUrl
RETURNS: Window handle if a secondary window is created, else NULL
COMMENTS:
Special processing for %SYSTEMROOT% anywhere in URL
Special processing for "hhwin:" and "file:"
MODIFICATION DATES:
04-Mar-1997 [ralphw]
***************************************************************************/
HWND JumpToUrl(IUnknown* pUnkOuter, HWND hwndParent, SITEMAP_ENTRY* pSiteMapEntry, CInfoType *pInfoType,
CSiteMap* pSiteMap, SITE_ENTRY_URL* pUrl, IWebBrowserAppImpl* pWebApp /* = NULL */)
{
ASSERT(pSiteMapEntry);
ASSERT(pSiteMap);
PCSTR pszFrame = pSiteMapEntry->GetFrameIndex() ?
pSiteMap->GetEntryFrame(pSiteMapEntry) : pSiteMap->GetFrameName();
PSTR pszInterTopic = NULL;
PCSTR pszUrl = NULL;
PCSTR pszSecondaryUrl = NULL;
if (!pUrl) {
if (pSiteMapEntry->fShowToEveryOne) {
pUrl = pSiteMapEntry->pUrls;
}
else { // choose based on Information Type
// If we get here, then we must match information types
ASSERT_COMMENT(pSiteMap->m_pInfoTypes,
"Info-type only URL specified without any user-specified Information Types");
// for (UINT j = 0; j < pSiteMap->m_cUrlEntry - (sizeof(URL) * 2); j++) {
INFOTYPE *pIT = pSiteMap->m_pInfoTypes;
for (int j=0; j<pSiteMap->InfoTypeSize()/4;j++)
{
pUrl = pSiteMap->AreTheseInfoTypesDefined(pSiteMapEntry, *pIT+(INFOTYPE)j, j);
if (pUrl)
break;
}
//ASSERT_COMMENT(pUrl, "This entry should not have been displayed, since there is no matching info type.");
if (!pUrl) {
AuthorMsg(IDS_HHA_NO_URL, "", hwndParent, NULL);
return NULL; // BUGBUG: we should notify the user
}
}
}
ASSERT(pUrl);
if (pUrl->urlPrimary) {
pszUrl = pSiteMap->GetUrlString(pUrl->urlPrimary);
if (pUrl->urlSecondary)
pszSecondaryUrl = pSiteMap->GetUrlString(pUrl->urlSecondary);
}
else if (pSiteMapEntry->pUrls->urlSecondary) {
pszUrl = pSiteMap->GetUrlString(pUrl->urlSecondary);
}
else { // no primary or secondary URL
AuthorMsg(IDS_HHA_NO_URL, "", hwndParent, NULL);
return NULL;
}
/*
* If the primary URL is a compiled HTML file, then first see if the
* compiled HTML file exists. If not, switch to the alternate URL.
*/
if (pszSecondaryUrl && IsCompiledHtmlFile(pszUrl, NULL)) {
CStr cszPath;
GetCompiledName(pszUrl, &cszPath);
if (!FindThisFile(NULL, cszPath, &cszPath, FALSE)) {
pszUrl = pszSecondaryUrl;
pszSecondaryUrl = NULL;
}
}
TrySecondary:
CStr cszUrl;
// Parse %SystemRoot%
PSTR psz = stristr(pszUrl, txtSysRoot);
if (psz) {
char szPath[MAX_PATH];
GetRegWindowsDirectory(szPath);
strcat(szPath, psz + strlen(txtSysRoot));
cszUrl = szPath;
pszUrl = cszUrl.psz;
}
PCSTR pszWindowName =
(pSiteMapEntry->GetWindowIndex() ?
pSiteMap->GetEntryWindow(pSiteMapEntry) :
pSiteMap->GetWindowName());
if (IsNonEmptyString(pszWindowName) && (IsEmptyString(pszFrame) ||
lstrcmpi(pszWindowName, pszFrame) != 0)) {
cszUrl = "hhwin:";
cszUrl += pszWindowName;
cszUrl += ":";
cszUrl += pszUrl;
pszUrl = cszUrl.psz;
}
/*
* If the URL is prefixed with hhwin: then we need to display this
* topic in a secondary window.
*/
CStr cszPrefix;
int cb = CompareSz(pszUrl, txtHHWin);
if (cb) {
pszUrl += cb;
CStr csz(pSiteMap->GetSiteMapFile() ? pSiteMap->GetSiteMapFile() : "");
CStr cszWindow(pszUrl);
PSTR pszTmp = StrChr(cszWindow, ':');
if (!pszTmp) {
// AuthorMsg(IDSHHA_INVALID_HHWIN, cszWindow);
return NULL; // REVIEW: should we notify the user?
}
*pszTmp = '\0';
pszUrl = FirstNonSpace(pszTmp + 1);
/*
* If we have a relative path specified, then we need to make it
* relative to the location of our sitemap file. Look for the last
* backslash or forward slash, and add our URL to the end.
*/
if (*pszUrl == '.') {
PSTR pszFilePortion = StrRChr(csz, '\\');
PSTR pszTmp = StrRChr(pszFilePortion ? pszFilePortion : csz, '/');
if (pszTmp)
pszFilePortion = pszTmp;
if (pszFilePortion) {
pszFilePortion[1] = '\0';
csz += pszUrl;
pszUrl = csz.psz;
}
}
if (!StrChr(pszUrl, ':') && pSiteMap->GetSiteMapFile() &&
IsCompiledHtmlFile(pSiteMap->GetSiteMapFile(), &cszPrefix)) {
PSTR pszSep = strstr(cszPrefix, txtDoubleColonSep);
ASSERT(pszSep);
if (!pszSep)
return NULL; // should never happen, but beats GPF if it does
strcpy(pszSep + 2, "/");
while (*pszUrl == '.')
pszUrl++;
if (*pszUrl == '/' || *pszUrl == '\\')
pszUrl++;
cszPrefix += pszUrl;
}
else
cszPrefix = pszUrl;
/*
Workaround for bug #5851
Once upon a time there was a bug in that window types were not CHM specific.
HTML Help shipped with this bug. Many people depended on the bug. In 1.1b we
fixed the bug and broke exisiting content. Bug 5851 is such a case. Before, the fix
below, the code was working EXACTLY as it should. However, we can't break those who
have shipped no matter how broken the previous version. So, here we actually add in
a bug to upbreak the already broken.
The fix is as follows. If you are jumping to an url and you provide a window type,
we check to see if there is a collection open which contains that url. If there is,
we check to see if that window type is defined by the master chm in the collection
and if it is open. If both of these are true, then we just navigate and don't create
a new window.
The result is that included CHMs cannot define windows with the same name as the MASTER CHM.
*/
// Is this file part of a collection?
CExCollection* pCollection = GetCurrentCollection(NULL, pszUrl);
if (pCollection)
{
// Is the window we are attempting to open defined by the master CHM?
CHHWinType* phh = FindWindowType(cszWindow.psz, NULL, pCollection->GetPathName());
// Does this window actually exist?
if (phh && IsWindow(phh->hwndHelp))
{
// We are going to reuse the exisiting window and just navigate.
if (pWebApp)
{
pWebApp->Navigate(cszPrefix, NULL, NULL, NULL, NULL);
return NULL ;
}
else if (IsWindow(hwndParent)) //--- Bug Fix for 7697: If hwndParent is NULL call OnDisplayTopic.
{
doHHWindowJump(cszPrefix, phh->hwndHelp);
return NULL ;
}
/* fall through */
}
}
// We will use a possiblity new window type, so call OnDisplayTopic.
cszPrefix += ">";
cszPrefix += cszWindow.psz;
return OnDisplayTopic(hwndParent, cszPrefix, 0);
}
/*
* If this is a file: URL, then first find out if the file
* actually exits. If not, then switch to the remote URL.
*/
cb = CompareSz(pszUrl, txtFileUrl);
if (cb) {
if ((pszInterTopic = StrChr(pszUrl, '#')))
*pszInterTopic = '\0';
if (GetFileAttributes(pszUrl + cb) == HFILE_ERROR) {
if (pszSecondaryUrl) {
pszUrl = pszSecondaryUrl;
pszSecondaryUrl = NULL;
goto TrySecondary;
}
AuthorMsg(IDS_HHA_NO_URL, "", hwndParent, NULL);
return NULL;
}
}
if (!pszInterTopic) {
if ((pszInterTopic = StrChr(pszUrl, '#')))
*pszInterTopic = '\0';
}
// Bug 7153, when this pointer gets moved pszInterTopic is pointing into the wrong string
// and therefore the fragment gets lost for this jump. Doing a sugical fix to reduce
// the massive regressions that could be caused by changing IsCompiledHtmlFile to correctly
// handle URL's with fragments.
BOOL bMovedPointer = FALSE;
if (IsCompiledHtmlFile(pszUrl, &cszPrefix))
{
bMovedPointer = TRUE;
pszUrl = cszPrefix.psz;
}
CWStr cwJump(pszUrl);
if (pszInterTopic)
{
*pszInterTopic = '#'; // restore the original line
if (bMovedPointer)
{
cszPrefix += pszInterTopic;
pszUrl = cszPrefix.psz;
}
}
CWStr cwLocation((pszInterTopic ? pszInterTopic : ""));
if (!pUnkOuter) {
/*
* I couldn't find anything to to give
* HlinkSimpleNavigateToString for pUnkOuter that wouldn't cause it
* to fire of a new instance of IE. Trouble is, doHHWindowJump ends
* up calling IWebBrowserAppImpl->Navigate who thinks all relative
* paths start with http: instead of the current root (which could
* be mk:). If we jump from a sitemap file, we fix that here.
*/
CStr cszPrefix;
if (!StrChr(pszUrl, ':') && pSiteMap &&
IsCompiledHtmlFile(pSiteMap->GetSiteMapFile(), &cszPrefix)) {
PSTR pszSep = strstr(cszPrefix, txtDoubleColonSep);
ASSERT(pszSep);
if (!pszSep)
return NULL; // should never happen, but beats GPF if it does
strcpy(pszSep + 2, "/");
while (*pszUrl == '.')
pszUrl++;
if (*pszUrl == '/' || *pszUrl == '\\')
pszUrl++;
cszPrefix += pszUrl;
if (pWebApp == NULL)
doHHWindowJump(cszPrefix, hwndParent);
else
pWebApp->Navigate(cszPrefix, NULL, NULL, NULL, NULL);
return NULL;
}
if (pWebApp == NULL)
doHHWindowJump(pszUrl, hwndParent);
else
pWebApp->Navigate(pszUrl, NULL, NULL, NULL, NULL);
return NULL;
}
CWStr cwFrame(pszFrame);
// REVIEW: if authoring is on, might want to call IsValidURL and
// let the author know if they messed up.
/*
* REVIEW: if we are inside of a compiled HTML file and this is a
* relative jump, then do our own checking first to avoid the browser
* error message.
*/
HRESULT hr = HlinkSimpleNavigateToString(cwJump, cwLocation,
cwFrame, pUnkOuter, NULL, NULL, 0, NULL);
/*
* If the jump failed, try the Remote jump (unless that's what we
* have already tried).
*/
if (!SUCCEEDED(hr)) {
if (pszSecondaryUrl) {
pszUrl = pszSecondaryUrl;
pszSecondaryUrl = NULL;
goto TrySecondary;
}
}
return NULL;
}
#if 0 // enable for subset filtering
BOOL ChooseInformationTypes(CInfoType *pInfoType, CSiteMap* pSiteMap, HWND hwndParent, CHtmlHelpControl* phhctrl, CHHWinType* phh)
#else
BOOL ChooseInformationTypes(CInfoType *pInfoType, CSiteMap* pSiteMap, HWND hwndParent, CHtmlHelpControl* phhctrl)
#endif
{
if (!pInfoType->HowManyInfoTypes()) {
#ifdef _DEBUG
MsgBox("No Information Types have been defined");
#endif
return FALSE;
}
CInfoTypePageContents* apwiz[MAX_CATEGORIES + 1];
int iMaxWizard = 0;
CMem mem((int)lcSize(pInfoType->m_pInfoTypes));
memcpy(mem.pb, pInfoType->m_pInfoTypes, lcSize(pInfoType->m_pInfoTypes));
CMem memE((int)lcSize(pInfoType->m_pInfoTypes));
memset(memE.pb, '\0', lcSize(pInfoType->m_pInfoTypes));
INFO_PARAM infoParam;
ZERO_STRUCTURE( infoParam );
infoParam.pTypicalInfoTypes = pInfoType->m_pTypicalInfoTypes ?
pInfoType->m_pTypicalInfoTypes : pInfoType->m_pInfoTypes;
infoParam.pInfoTypes = (INFOTYPE*) mem.pb;
#if 0 // enable for subset filtering
infoParam.pExclusive = (INFOTYPE*) memE.pb;
#endif
infoParam.pSiteMap = pSiteMap;
infoParam.idDlgTemplate = IDWIZ_INFOTYPE_CUSTOM_INCLUSIVE;
infoParam.fExclusive = FALSE;
infoParam.idNextPage = 0;
infoParam.idPreviousPage = 0;
infoParam.iCategory = -1;
infoParam.fAll = FALSE;
infoParam.fTypical = TRUE;
infoParam.fCustom = FALSE;
infoParam.pInfoType = pInfoType;
CPropSheet cprop(NULL, PSH_WIZARD, hwndParent);
#if 0 // enable for subset filtering
CWizardIntro wizIntro(phh? phh->m_phmData->m_pTitleCollection : NULL, &infoParam);
CInfoWizFinish wizFinish(phh, &infoParam);
#else
CWizardIntro wizIntro(phhctrl, &infoParam);
CInfoWizFinish wizFinish(phhctrl, &infoParam);
#endif
int type;
if ( pInfoType->HowManyCategories() > 0 )
{
for (int CatCount = 0; CatCount<pInfoType->HowManyCategories(); CatCount++)
{
BOOL fAllHidden = TRUE;
infoParam.iCategory = CatCount;
infoParam.pagebits = pInfoType->m_itTables.m_aCategories[CatCount].pInfoType;
type = pInfoType->GetFirstCategoryType(CatCount); // check all the IT's to see if there is an exclusive type in the category.
if ( type == -1 )
continue; // we dont want categories without information types in them
while ( type != -1 )
{
if ( !pInfoType->IsHidden ( type ) )
fAllHidden = FALSE;
if ( pInfoType->IsExclusive(type) )
{
infoParam.idDlgTemplate = IDWIZ_INFOTYPE_CUSTOM_EXCLUSIVE;
infoParam.fExclusive = TRUE;
break;
}
type = pInfoType->GetNextITinCategory();
}
if ( !fAllHidden )
#if 0 // enable for subset filtering
apwiz[iMaxWizard++] = new CInfoTypePageContents(phh->m_phmData->m_pTitleCollection, &infoParam);
#else
apwiz[iMaxWizard++] = new CInfoTypePageContents(phhctrl, &infoParam);
#endif
infoParam.idDlgTemplate = IDWIZ_INFOTYPE_CUSTOM_INCLUSIVE;
infoParam.fExclusive = FALSE;
}
iMaxWizard--;
}
else
{ // there are no categories
if ( pInfoType->HowManyInfoTypes() > 0 )
{
infoParam.iCategory = -1;
infoParam.idNextPage = CInfoWizFinish::IDD;
if ( pInfoType->GetFirstExclusive() != -1 ) // we have a set of exclusive ITs
{
infoParam.idDlgTemplate = IDWIZ_INFOTYPE_CUSTOM_EXCLUSIVE;
infoParam.fExclusive = TRUE;
infoParam.pagebits = pInfoType->m_itTables.m_pExclusive;
#if 0 // enable for subset filtering
apwiz[iMaxWizard] = new CInfoTypePageContents(phh->m_phmData->m_pTitleCollection, &infoParam);
#else
apwiz[iMaxWizard] = new CInfoTypePageContents(phhctrl, &infoParam);
#endif
}
else // we have a set of inclusive ITs
{
infoParam.pagebits = NULL; // look in CInfoType for Inclusive IT.
#if 0 // enable for subset filtering
apwiz[iMaxWizard] = new CInfoTypePageContents(phh ? phh->m_phmData->m_pTitleCollection : NULL, &infoParam);
#else
apwiz[iMaxWizard] = new CInfoTypePageContents(phhctrl, &infoParam);
#endif
}
}
}
cprop.AddPage(&wizIntro);
ASSERT_COMMENT(iMaxWizard >= 0, "No information types specified")
for (int i = 0; i <= iMaxWizard; i++)
cprop.AddPage(apwiz[i]);
cprop.AddPage(&wizFinish);
if (phhctrl)
phhctrl->ModalDialog(TRUE);
BOOL fResult = cprop.DoModal();
if (phhctrl)
phhctrl->ModalDialog(FALSE);
// Free the Wizzard Pages
for (int j=0; j<iMaxWizard; j++)
{
if ( apwiz[j] >= 0 )
delete apwiz[j];
}
if (!fResult)
return FALSE;
#if 1 // disable for subset filtering
memcpy(pInfoType->m_pInfoTypes, infoParam.pInfoTypes, lcSize(pInfoType->m_pInfoTypes));
#endif
return TRUE;
}
//=--------------------------------------------------------------------------=
// ActiveX Event Firing
//=--------------------------------------------------------------------------=
static VARTYPE rgBstr[] = { VT_BSTR };
typedef enum {
HHCtrlEvent_Click = 0,
} HHCTRLEVENTS;
static EVENTINFO rgHHCtrlEvents [] = {
{ DISPID_ONCLICK, 1, rgBstr } // Click method
};
//=--------------------------------------------------------------------------=
// CHtmlHelpControl Class
//=--------------------------------------------------------------------------=
AUTO_CLASS_COUNT_CHECK( CHtmlHelpControl );
CHtmlHelpControl::CHtmlHelpControl(IUnknown *pUnkOuter)
: CInternetControl(pUnkOuter, OBJECT_TYPE_CTLHHCTRL, (IDispatch *)this)
{
memset(&m_state, 0, sizeof(HHCTRLCTLSTATE));
m_state.bmpPath = 0;
m_clrFont = CLR_INVALID;
m_hpadding = -1;
m_vpadding = -1;
m_readystate = bdsNoBitsYet;
m_ptoc = NULL;
m_pindex = NULL;
m_hfont = NULL;
bSharedFont = FALSE;
m_pszBitmap = NULL;
m_pszWebMap = NULL;
m_pszActionData = NULL;
m_pwszButtonText = NULL;
m_fButton = FALSE;
m_fBuiltInImage = FALSE;
m_ptblItems = NULL;
m_hbrBackGround = NULL;
m_hImage = NULL;
m_pSiteMap = NULL;
m_hwndHelp = NULL;
m_pszEventString = NULL;
m_pWebBrowserApp = NULL;
m_ptblTitles = NULL;
m_ptblURLs = NULL;
m_ptblLocations = NULL;
m_pszFrame = NULL;
m_pszWindow = NULL;
m_pszDefaultTopic = NULL;
m_pInfoType = NULL;
if (!g_hmodHHA && !g_fTriedHHA)
LoadHHA(NULL, _Module.GetModuleInstance());
m_pSelectedIndexInfoTypes = NULL;
m_lpfnlStaticTextControlWndProc = NULL;
m_hwndDisplayButton = NULL;
m_dc = NULL;
m_idBitmap = -1;
m_fWinHelpPopup = 0;
m_fPopupMenu = 0;
memset( &m_rcButton, 0, sizeof(m_rcButton) );
m_fIcon = 0;
m_oldSize = 0;
m_clrFontDisabled = GetSysColor(COLOR_GRAYTEXT);
m_clrFontLink = RGB(0,0,255);
m_clrFontLinkVisited = RGB(128,0,128);
m_clrFontHover = RGB(255,0,0);
m_szFontSpec[0] = '\0';
m_Charset = -1;
m_pIFont = 0;
}
CHtmlHelpControl::~CHtmlHelpControl ()
{
if (IsValidWindow(m_hwndHelp))
DestroyWindow(m_hwndHelp);
if ( m_pIFont && m_hfont )
{
m_pIFont->AddRefHfont(m_hfont);
m_pIFont->ReleaseHfont(m_hfont);
m_pIFont->Release();
m_hfont = 0;
}
if (m_state.bmpPath)
delete m_state.bmpPath;
if (m_hfont && !bSharedFont )
DeleteObject(m_hfont);
if (m_pszActionData)
lcFree(m_pszActionData);
if ( m_pindex )
delete m_pindex;
if ( m_ptoc )
delete m_ptoc;
if (m_pwszButtonText)
lcFree(m_pwszButtonText);
if (m_pszBitmap)
lcFree(m_pszBitmap);
if (m_pszWebMap)
lcFree(m_pszWebMap);
if (m_ptblItems)
delete m_ptblItems;
if (m_pSiteMap)
delete m_pSiteMap;
if (m_hbrBackGround)
DeleteObject((HGDIOBJ) m_hbrBackGround);
if (!m_fBuiltInImage && m_hImage)
DeleteObject(m_hImage);
if (m_pszEventString)
lcFree(m_pszEventString);
if (m_pszFrame)
lcFree(m_pszFrame);
if (m_pszWindow)
lcFree(m_pszWindow);
if (m_pszDefaultTopic)
lcFree(m_pszDefaultTopic);
if (m_pWebBrowserApp)
delete m_pWebBrowserApp;
if (m_ptblTitles)
delete m_ptblTitles;
if (m_ptblURLs)
delete m_ptblURLs;
if ( m_ptblLocations )
delete m_ptblLocations;
#if 0
if (m_dibFile)
delete m_dibFile;
if (m_dib)
delete m_dib;
#endif
#if 0 // it appears that someone else (IE maybe) is destroying this for us
if( m_hwndDisplayButton && IsValidWindow(m_hwndDisplayButton) )
if( DestroyWindow( m_hwndDisplayButton ) == 0 )
DWORD dwError = GetLastError();
#endif
#ifdef _DEBUG
m_ptoc = NULL;
m_pindex = NULL;
m_state.bmpPath = 0;
m_hfont = 0;
m_pszActionData = 0;
m_pwszButtonText = 0;
m_pszBitmap = 0;
m_pszWebMap = 0;
m_ptblItems = 0;
m_pSiteMap = 0;
m_hImage = NULL;
m_hbrBackGround = 0;
m_pszEventString = 0;
m_pszFrame = 0;
m_pszWindow = 0;
m_pWebBrowserApp = 0;
m_ptblTitles = 0;
m_ptblURLs = 0;
m_ptblLocations = 0;
#endif
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::Create
//=--------------------------------------------------------------------------=
//
IUnknown* CHtmlHelpControl::Create(IUnknown *pUnkOuter)
{
// make sure we return the private unknown so that we support aggregation
// correctly!
CHtmlHelpControl *pNew = new CHtmlHelpControl(pUnkOuter);
return pNew->PrivateUnknown();
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::RegisterClassData
//=--------------------------------------------------------------------------=
//
BOOL CHtmlHelpControl::RegisterClassData(void)
{
WNDCLASS wndclass;
ZeroMemory(&wndclass, sizeof(WNDCLASS));
wndclass.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
wndclass.lpfnWndProc = CInternetControl::ControlWindowProc;
wndclass.hInstance = _Module.GetModuleInstance();
switch (m_action) {
// Non-UI or specialized UI
case ACT_CONTENTS:
case ACT_INDEX:
case ACT_SPLASH:
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
break;
default:
if (!m_fButton)
wndclass.hCursor = LoadCursor(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDCUR_HAND));
else
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
break;
}
wndclass.hbrBackground =
m_hbrBackGround ? m_hbrBackGround : (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszClassName = WNDCLASSNAMEOFCONTROL(OBJECT_TYPE_CTLHHCTRL);
DBWIN("Class registered");
return RegisterClass(&wndclass);
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::ShouldCreateWindow
//=--------------------------------------------------------------------------=
//
// Essintialy called from Controls DoVerb() on OLEIVERB_SHOW, OLEIVERB_UIACTIVATE,
// and OLEIVERB_INPLACEACTIVATE calls. We can safely implement code in this function
// to cancel the creation of an ole control window.
//
// Output:
// BOOL - false don't create the control.
//
BOOL CHtmlHelpControl::ShouldCreateWindow()
{
if ( (m_action == ACT_CONTENTS) || (m_action == ACT_INDEX) || (m_action == ACT_SPLASH))
return TRUE;
else return m_fButton;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::BeforeCreateWindow
//=--------------------------------------------------------------------------=
// called just before the window is created. Great place to set up the
// window title, etc, so that they're passed in to the call to CreateWindowEx.
// speeds things up slightly.
//
// Parameters:
// DWORD * - [out] dwWindowFlags
// DWORD * - [out] dwExWindowFlags
// LPSTR - [out] name of window to create
//
// Output:
// BOOL - false means fatal error
BOOL CHtmlHelpControl::BeforeCreateWindow(DWORD *pdwWindowStyle,
DWORD *pdwExWindowStyle, LPSTR pszWindowTitle)
{
/*
* TODO: users should set the values of *pdwWindowStyle,
* *pdwExWindowStyle, and pszWindowTitle so that the call to
* CreateWindowEx can use them. setting them here instead of calling
* SetWindowStyle in WM_CREATE is a huge perf win if you don't use this
* function, then you can probably just remove it.
*/
switch (m_action)
{
case ACT_CONTENTS:
break;
case ACT_INDEX:
*pdwExWindowStyle = WS_EX_CONTROLPARENT; // allow tab key in children
break;
case ACT_SPLASH:
break;
default:
break;
}
return TRUE;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::AfterCreateWindow
//=--------------------------------------------------------------------------=
//
BOOL CHtmlHelpControl::AfterCreateWindow()
{
switch (m_action) {
case ACT_CONTENTS:
if (!m_ptblItems) {
AuthorMsg(IDS_MUST_SPECIFY_HHC);
break;
}
LoadContentsFile(
IsEmptyString(m_pszWebMap) ? m_ptblItems->GetPointer(1) : m_pszWebMap);
m_ptoc->SetStyles(m_flags[0], m_flags[1]);
if (IsValidWindow(m_ptoc->m_hwndTree))
return TRUE;
if (!m_ptoc->Create(m_hwnd)) {
// BUGBUG: default to a generic display
return FALSE; // window can't be created
}
m_ptoc->m_fHack = FALSE;
m_ptoc->InitTreeView();
return TRUE;
case ACT_INDEX:
if (!m_ptblItems) {
AuthorMsg(IDS_MUST_SPECIFY_HHC);
break;
}
LoadIndexFile(IsEmptyString(m_pszWebMap) ? m_ptblItems->GetPointer(1) : m_pszWebMap);
if ( m_pindex )
m_pindex->m_phhctrl = this;
if (!m_pindex || !m_pindex->Create(m_hwnd)) {
// BUGBUG: default to a generic display
return FALSE; // window can't be created
}
return TRUE;
case ACT_SPLASH:
CreateSplash();
return TRUE;
case ACT_TEXT_POPUP:
case ACT_ALINK:
case ACT_KLINK: {
IHTMLDocument2* pHTMLDocument2 = NULL;
if( m_pWebBrowserApp ) {
LPDISPATCH lpDispatch = m_pWebBrowserApp->GetDocument();
if( lpDispatch && SUCCEEDED(lpDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHTMLDocument2))) {
m_clrFontDisabled = GetSysColor(COLOR_GRAYTEXT);
VARIANT varColor;
::VariantInit(&varColor);
if( SUCCEEDED( pHTMLDocument2->get_linkColor(&varColor) ) )
{
m_clrFontLink = IEColorToWin32Color(varColor.puiVal);
VariantClear(&varColor); // Delete memory allocated.
}
::VariantInit(&varColor);
if( SUCCEEDED( pHTMLDocument2->get_vlinkColor(&varColor) ) )
{
m_clrFontLinkVisited = IEColorToWin32Color(varColor.puiVal);
VariantClear(&varColor); // Delete memory allocated.
}
#if 0 // [PaulTi] I don't know how to query this from IE4 -- so red it is for now
::VariantInit(&varColor);
if( SUCCEEDED( pHTMLDocument2->get_alinkColor(&varColor) ) )
{
m_clrFontHover = IEColorToWin32Color(varColor.puiVal);
VariantClear(&varColor); // Delete memory allocated.
}
#endif
}
if( lpDispatch )
lpDispatch->Release();
}
if( pHTMLDocument2 )
pHTMLDocument2->Release();
}
// intentionally fall thru
default:
if (m_fButton) {
if (!CreateOnClickButton()) {
// BUGBUG: default to a generic display
return FALSE; // window can't be created
}
{
SIZE size;
size.cx = RECT_WIDTH(m_rcButton);
size.cy = RECT_HEIGHT(m_rcButton);
SetControlSize(&size);
}
ShowWindow(m_hwndDisplayButton, SW_SHOW);
break;
}
break;
}
// REVIEW: not necessary if we aren't using 256-color bitmaps. Also,
// shouldn't we be destroying this palette when we are done with it?
#if 0
m_dc = ::GetDC(m_hwnd);
HPALETTE hpal = ::CreateHalftonePalette(m_dc);
::SelectPalette(m_dc, hpal, TRUE);
#endif
UpdateImage();
return TRUE;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::LoadTextState
//=--------------------------------------------------------------------------=
// load in our text state for this control.
//
// Parameters:
// IPropertyBag * - [in] property bag to read from
// IErrorLog * - [in] errorlog object to use with proeprty bag
//
// Output:
// HRESULT
//
// Notes:
// - NOTE: if you have a binary object, then you should pass an unknown
// pointer to the property bag, and it will QI it for IPersistStream, and
// get said object to do a Load()
//
//
// We expect to see a single Name=Command, Value=command type followed by
// any number of Name=Item<n> where n is any sequential digit. Note that in
// some cases, the command value will include optional information
// affecting the command.
//
// WinHelp Popup, help file
// item1 = number or string for WinHelp popup
// Text Popup
// item1 = popup text
//
// AboutBox, title
// item 1-3 = lines 1-3
//
// Related Topics[, Dialog | Menu] (default is Dialog)
// item1=title=url
// ...
// item<n>=title=url
// Contents, file.cnt
// Index, file.kwd
//
STDMETHODIMP CHtmlHelpControl::LoadTextState(IPropertyBag *pPropertyBag,
IErrorLog *pErrorLog)
{
VARIANT v;
HRESULT hr;
ZeroMemory(&v, sizeof(VARIANT));
VariantInit(&v);
v.vt = VT_BSTR;
v.bstrVal = NULL;
CHHWinType* phh = NULL;
HWND hWnd;
ZeroMemory(m_flags, sizeof(m_flags));
m_flags[2] = (DWORD) -1;
if ( (hWnd = GetHtmlHelpFrameWindow()) ) // Tunnels from the ActiveX control through IE to get to the HWND of HHCTRL.
{
if ( (phh = FindHHWindowIndex(hWnd)) )
m_Charset = phh->GetContentCharset();
}
m_imgType = IMG_BITMAP; // default image type
// Get the command for this control to perform
hr = pPropertyBag->Read(txtwCommand, &v, pErrorLog);
if (SUCCEEDED(hr)) {
MAKE_ANSIPTR_FROMWIDE(psz, v.bstrVal);
if (!g_fIE3) // fix for bug 5664, 5665
VariantClear(&v); // Delete memory allocated for BSTR
/*
#ifdef _DEBUG
{
char szMsg[512];
wsprintf(szMsg, "Command: %s\r\n", psz);
SendStringToParent(szMsg);
}
#endif
*/
// truncate command at 255 characters
//
if(strlen(psz) > 255)
psz[255] = 0;
// Always save a copy in this string...
//
lstrcpy(m_szRawAction, psz);
if (isSameString(psz, txtWinHelp)) {
// Let author know we're initialized
m_action = ACT_WINHELP;
m_fWinHelpPopup = ( stristr(psz, txtPopup) != NULL );
}
else if (isSameString(psz, txtRelatedTopics)) {
m_action = ACT_RELATED_TOPICS;
SetActionData(psz);
m_fPopupMenu = isSameString(m_pszActionData, txtMenu);
if (!m_pSiteMap)
m_pSiteMap = new CSiteMap(MAX_RELATED_ENTRIES);
}
else if (isSameString(psz, txtActKLink)) {
m_action = ACT_KLINK;
SetActionData(psz);
m_fPopupMenu = isSameString(m_pszActionData, txtMenu);
}
else if (isSameString(psz, txtActALink)) {
m_action = ACT_ALINK;
SetActionData(psz);
m_fPopupMenu = isSameString(m_pszActionData, txtMenu);
}
else if (isSameString(psz, txtActSample)) {
m_action = ACT_SAMPLE;
SetActionData(psz);
m_fPopupMenu = isSameString(m_pszActionData, txtMenu);
}
else if (isSameString(psz, txtKeywordSearch)) {
m_action = ACT_KEYWORD_SEARCH;
SetActionData(psz);
m_fPopupMenu = isSameString(m_pszActionData, txtMenu);
if (!m_pSiteMap)
m_pSiteMap = new CSiteMap(MAX_KEYSEARCH_ENTRIES);
}
else if (isSameString(psz, txtShortcut)) {
m_action = ACT_SHORTCUT;
}
else if (isSameString(psz, txtContents)) {
m_flags[0] = WS_EX_CLIENTEDGE;
m_flags[1] = DEFAULT_TOC_STYLES;
m_action = ACT_CONTENTS;
m_imgType = IMG_CHILD_WINDOW;
SetActionData(psz);
ProcessPadding(m_pszActionData);
}
else if (isSameString(psz, txtIndex))
{
// Let author know we're initialized
m_flags[0] = WS_EX_WINDOWEDGE;
m_action = ACT_INDEX;
m_imgType = IMG_CHILD_WINDOW;
SetActionData(psz);
ProcessPadding(m_pszActionData);
// see if the client has requested RTL layout
//
if(SUCCEEDED(pPropertyBag->Read(L"LayoutRTL", &v, pErrorLog)))
{
if(v.bstrVal)
{
// convert the value to ANSI
//
char szValue[32];
WideCharToMultiByte(CP_ACP, 0, v.bstrVal, -1, szValue, sizeof(szValue), NULL, NULL);
szValue[sizeof(szValue) - 1] = 0;
VariantClear(&v);
if(!stricmp(szValue, "TRUE"))
{
// turn on RTL styles
//
g_RTL_Mirror_Style = 0;
g_RTL_Style = WS_EX_RTLREADING | WS_EX_RIGHT;
g_fuBiDiMessageBox = MB_RIGHT | MB_RTLREADING;
g_fBiDi = TRUE;
}
}
}
}
else if (isSameString(psz, txtHhCtrlVersion)) {
m_action = ACT_HHCTRL_VERSION;
}
else if (isSameString(psz, txtSplash)) {
m_action = ACT_SPLASH;
}
else if (isSameString(psz, txtTCard)) {
m_action = ACT_TCARD;
}
else if (isSameString(psz, txtClose)) {
m_action = ACT_CLOSE;
}
else if (isSameString(psz, txtHHWinPrint)) {
m_action = ACT_HHWIN_PRINT;
}
else if (isSameString(psz, txtMinimize)) {
m_action = ACT_MINIMIZE;
}
else if (isSameString(psz, txtMaximize)) {
m_action = ACT_MAXIMIZE;
}
else if (isSameString(psz, txtAboutBox)) {
// Let author know we're initialized
m_action = ACT_ABOUT_BOX;
SetActionData(psz);
}
else {
AuthorMsg(IDS_INVALID_INITIALIZER, psz);
return E_INVALIDARG;
}
//
// DANGER! DANGER! DANGER! DANGER! DANGER! DANGER! DANGER!
//
// [PaulTi] so far the code block below has been accidently
// removed twice with new checkins and has totally broken A/KLinks.
//
// Before ANYONE changes this code you must check with PaulTi
// to make sure you have not caused this code to break again
// (three strikes and you are out!).
//
// get the remaining arguments
PSTR pszArguments = StrChr(psz, ',');
if( pszArguments && *pszArguments == ',' ) // skip over the comma
pszArguments++;
pszArguments = FirstNonSpace(pszArguments); // skip over whitespace
while (pszArguments && *pszArguments) {
if (isSameString(pszArguments, txtBitmap)) {
// Skip to the filename
PSTR pszFile = FirstNonSpace(pszArguments + strlen(txtBitmap));
pszArguments = StrChr(pszFile, ',');
if( *pszArguments == ',' )
pszArguments++; // skip over the comma
if (pszArguments) {
*pszArguments = '\0';
pszArguments = FirstNonSpace(pszArguments + 1);
}
RemoveTrailingSpaces(pszFile);
m_pszBitmap = lcStrDup(pszFile);
}
// BUGBUG: finish processing the rest of the commands
else {
if( pszArguments ) {
pszArguments = StrChr(pszArguments, ',');
if( pszArguments && *pszArguments == ',' ) // skip over the comma
pszArguments++;
pszArguments = FirstNonSpace(pszArguments);
}
}
}
}
else { // Command not specified
AuthorMsg(IDS_MISSING_COMMAND, "");
return E_INVALIDARG;
}
// shanemc 7883 - Support bgcolor for index
if (ACT_INDEX == m_action)
{
VARIANT v;
ZeroMemory(&v, sizeof(VARIANT));
VariantInit(&v);
v.vt = VT_BSTR;
v.bstrVal = NULL;
// Get the background color
HRESULT hr = pPropertyBag->Read(txtwBgColor, &v, pErrorLog);
if (SUCCEEDED(hr) && (v.vt == VT_BSTR)) {
MAKE_ANSIPTR_FROMWIDE(psz, v.bstrVal);
VariantClear(&v); // Delete memory allocated for BSTR
// default to button face color
ULONG ulColor = GetSysColor(COLOR_BTNFACE);
// HACK for special case requested by Millennium team: Check for button face color.
// Really we should check for all the system and named colors supported by IE, but
// this isn't an ideal world.
if (strcmpi(psz, "buttonface") == 0) {
// Don't do anything; default is correct color
}
else {
// Format is like Web--6 hex digits
if ('#' == *psz) psz++; // skip optional #
// Only override default if we get a valid number.
if (isxdigit(*psz)) {
ulColor = strtoul(psz, NULL, 16);
// Need to flip the bytes from BGR to RGB
BYTE bB = static_cast<BYTE>(ulColor & 0xff);
BYTE bG = static_cast<BYTE>((ulColor >> 8) & 0xff);
BYTE bR = static_cast<BYTE>(ulColor >> 16);
ulColor = RGB(bR, bG, bB);
}
}
if (m_hbrBackGround) DeleteObject((HGDIOBJ) m_hbrBackGround);
m_hbrBackGround = CreateSolidBrush(ulColor);
m_clrFontHover = ulColor; // HACK!!! this color not used for Index
// (I don't want to change hhctrl.h to add new var)
}
}
// end shanemc 7883
// Read all item data starting with Item1, then Item2, etc. until
// an item isn't found.
char szBuf[20];
int iItem = 1;
for (;;) {
wsprintf(szBuf, txtItem, iItem++);
WCHAR uniBuf[sizeof(szBuf) * 2];
MultiByteToWideChar(CP_ACP, 0, szBuf, -1, uniBuf, sizeof(uniBuf));
// REVIEW: Do we need to convert to unicode?
hr = pPropertyBag->Read(uniBuf, &v, pErrorLog);
if (SUCCEEDED(hr)) {
// Choose the amount of memory the CTable needs to reserve based
// on the command
if (m_ptblItems == NULL) {
switch (m_action) {
case ACT_ABOUT_BOX:
case ACT_CONTENTS:
case ACT_INDEX:
case ACT_WINHELP:
case ACT_SPLASH:
case ACT_SHORTCUT:
m_ptblItems = new CTable(4096);
break;
case ACT_CLOSE:
case ACT_MINIMIZE:
case ACT_MAXIMIZE:
case ACT_TCARD: // data stored in m_pszActionData, not m_ptblItems
case ACT_HHWIN_PRINT:
break;
case ACT_KLINK:
case ACT_ALINK:
m_ptblItems = new CTable(1024 * 1024);
break;
case ACT_RELATED_TOPICS:
case ACT_KEYWORD_SEARCH:
case ACT_TEXT_POPUP:
default:
m_ptblItems = new CTable(256 * 1024);
break;
}
}
CStr csz(v.bstrVal);
if ( !g_fIE3 ) // fix for bug 5661 ...
VariantClear(&v); // Delete memory allocated for BSTR
if (m_action == ACT_RELATED_TOPICS) {
// Format is "title;url1;url2
PSTR pszUrl = StrChr(csz, ';');
if (pszUrl)
*pszUrl++ = '\0';
SITEMAP_ENTRY* pSiteMapEntry = m_pSiteMap->AddEntry();
if (pSiteMapEntry != NULL)
{
ClearMemory(pSiteMapEntry, sizeof(SITEMAP_ENTRY));
pSiteMapEntry->pszText = m_pSiteMap->StrDup((csz));
if (pszUrl) {
CSiteEntryUrl SiteUrl( sizeof(SITE_ENTRY_URL) );
PSTR psz = pszUrl;
pszUrl = StrChr(psz, ';');
if (pszUrl)
*pszUrl++ = '\0';
SiteUrl.m_pUrl->urlPrimary = m_pSiteMap->AddUrl(psz);
if (pszUrl)
SiteUrl.m_pUrl->urlSecondary = m_pSiteMap->AddUrl(pszUrl);
SiteUrl.SaveUrlEntry(m_pSiteMap, pSiteMapEntry);
}
}
else
break;
}
else if (m_action == ACT_TCARD) {
if(!m_pszActionData)
csz.TransferPointer(&m_pszActionData);
}
else if (m_ptblItems) { // not a related topic, so treat normally
//
// DANGER! DANGER! DANGER! DANGER! DANGER! DANGER! DANGER!
//
// [PaulTi] so far the code block below has been accidently
// removed twice with new checkins and has totally broken A/KLinks.
//
// Before ANYONE changes this code you must check with PaulTi
// to make sure you have not caused this code to break again
// (three strikes and you are out!).
//
LPSTR psz = FirstNonSpace(csz);
m_ptblItems->AddString(psz?psz:""); // Save the item
}
/* csz can be significantly larger than 512 characters.
#ifdef _DEBUG
if (csz.psz) {
char szMsg[512];
wsprintf(szMsg, "%s: %s\r\n", szBuf, csz.psz);
SendStringToParent(szMsg);
}
#endif
*/
}
else
break;
}
if (m_action != ACT_CONTENTS && m_action != ACT_INDEX) {
hr = pPropertyBag->Read(txtwButton, &v, pErrorLog);
if (SUCCEEDED(hr)) {
CStr csz(v.bstrVal);
int cb;
if ((cb = CompareSz(csz, txtText)))
m_pwszButtonText = lcStrDupW(v.bstrVal + cb);
else if ((cb = CompareSz(csz, txtBitmap))) {
m_pszBitmap = lcStrDup(FirstNonSpace(csz.psz + cb));
m_flags[1] |= BS_BITMAP;
m_fIcon = FALSE;
}
else if ((cb = CompareSz(csz, txtIcon))) {
m_pszBitmap = lcStrDup(FirstNonSpace(csz.psz + cb));
m_flags[1] |= BS_ICON;
m_fIcon = TRUE;
}
else // default to a text button
m_pwszButtonText = lcStrDupW(v.bstrVal);
m_fButton = TRUE;
m_imgType = IMG_BUTTON;
VariantClear(&v); // Delete memory allocated for BSTR
}
else { // Button, Text and Icon are mutually exclusive
hr = pPropertyBag->Read(txtwText, &v, pErrorLog);
if (SUCCEEDED(hr)) {
MAKE_ANSIPTR_FROMWIDE(psz, v.bstrVal);
int cb;
if ((cb = CompareSz(psz, txtText)))
{
WCHAR* pwsz = v.bstrVal + cb;
while ( *pwsz == L' ' )
++pwsz;
m_pwszButtonText = lcStrDupW(pwsz);
}
else if ((cb = CompareSz(psz, txtBitmap))) {
m_pszBitmap = lcStrDup(FirstNonSpace(psz + cb));
m_fIcon = FALSE;
m_flags[1] |= SS_BITMAP;
}
else if ((cb = CompareSz(psz, txtIcon))) {
m_pszBitmap = lcStrDup(FirstNonSpace(psz + cb));
m_fIcon = TRUE;
m_flags[1] |= SS_ICON;
}
else if (*psz) // no text is a chiclet button
AuthorMsg(IDS_INVALID_BUTTON_CMD, psz);
m_fButton = TRUE;
m_imgType = IMG_TEXT;
m_flags[1] |= SS_NOTIFY | (m_pszBitmap ? 0 : SS_OWNERDRAW);
VariantClear(&v); // Delete memory allocated for BSTR
}
}
}
hr = pPropertyBag->Read(txtwImage, &v, pErrorLog);
if (SUCCEEDED(hr)) {
CStr csz(v.bstrVal);
VariantClear(&v); // Delete memory allocated for BSTR
SzTrimSz(csz);
if (m_pszBitmap)
lcFree(m_pszBitmap);
csz.TransferPointer(&m_pszBitmap);
}
hr = pPropertyBag->Read(txtwWebMap, &v, pErrorLog);
if (SUCCEEDED(hr)) {
MAKE_ANSIPTR_FROMWIDE(psz, v.bstrVal);
VariantClear(&v); // Delete memory allocated for BSTR
if (m_pszWebMap)
lcFree(m_pszWebMap);
m_pszWebMap = lcStrDup(FirstNonSpace(psz));
RemoveTrailingSpaces((PSTR) m_pszWebMap);
}
hr = pPropertyBag->Read(txtwFont, &v, pErrorLog);
if (SUCCEEDED(hr)) {
MAKE_ANSIPTR_FROMWIDE(psz, v.bstrVal);
VariantClear(&v); // Delete memory allocated for BSTR
if (m_hfont)
DeleteObject(m_hfont);
m_hfont = CreateUserFont(psz, &m_clrFont, NULL, m_Charset);
lstrcpy(m_szFontSpec, psz);
}
/*
* Flags should be read after any command, since commands may set
* default flag values.
*/
hr = pPropertyBag->Read(txtwFlags, &v, pErrorLog);
if ( SUCCEEDED(hr) && (v.vt == VT_BSTR) ) {
MAKE_ANSIPTR_FROMWIDE(psz, v.bstrVal);
VariantClear(&v); // Delete memory allocated for BSTR
for (int i = 0; i < MAX_FLAGS; i++) {
while (*psz && (*psz < '0' || *psz > '9') && *psz != ',')
psz++;
if (!*psz)
break;
if (*psz != ',')
m_flags[i] = Atoi(psz);
psz = strchr(psz, ',');
if (!psz)
break;
psz = FirstNonSpace(psz + 1);
}
if (m_flags[2] != (DWORD) -1 && m_action != ACT_KLINK && m_action != ACT_ALINK) {
if (m_hbrBackGround)
DeleteObject((HGDIOBJ) m_hbrBackGround);
m_hbrBackGround = CreateSolidBrush(m_flags[2]);
}
}
hr = pPropertyBag->Read(txtwFrame, &v, pErrorLog);
if (SUCCEEDED(hr)) {
CStr csz(v.bstrVal);
VariantClear(&v); // Delete memory allocated for BSTR
if (m_pSiteMap)
m_pSiteMap->SetFrameName(csz);
else {
if (m_pszFrame)
lcFree(m_pszFrame);
csz.TransferPointer(&m_pszFrame);
}
}
hr = pPropertyBag->Read(txtwWindow, &v, pErrorLog);
if (SUCCEEDED(hr)) {
CStr csz(v.bstrVal);
VariantClear(&v); // Delete memory allocated for BSTR
if (m_pSiteMap)
m_pSiteMap->SetWindowName(csz);
else {
if (m_pszWindow)
lcFree(m_pszWindow);
csz.TransferPointer(&m_pszWindow);
}
}
hr = pPropertyBag->Read(txtwDefaultTopic, &v, pErrorLog);
if (SUCCEEDED(hr)) {
CStr csz(v.bstrVal);
VariantClear(&v); // Delete memory allocated for BSTR
csz.TransferPointer(&m_pszDefaultTopic);
}
//
// We need to create an appropiate font for the display of alinks/klinks/dynalinks. This
// needs to be a content font rather than a UI font.
//
if (! m_hfont )
{
if ( phh )
{
m_hfont = phh->GetContentFont();
bSharedFont = TRUE;
}
if (! m_hfont ) // This code asks our container (IE) for a resolable font.
{
if ( GetAmbientFont(&m_pIFont) )
{
m_pIFont->get_hFont(&m_hfont);
m_pIFont->AddRefHfont(m_hfont);
#ifdef _DEBUG
LOGFONT lf ;
int r = GetObject(m_hfont, sizeof(lf), &lf) ;
#endif
}
}
}
if (! m_hfont )
m_hfont = CreateUserFont(GetStringResource(IDS_DEFAULT_CONTENT_FONT)); // Last resort!
//
// Now figure out a charset identifier and codepage...
//
CHARSETINFO cs;
if ( phh )
{
m_Charset = phh->GetContentCharset();
if ( TranslateCharsetInfo ((DWORD *)(DWORD_PTR)MAKELONG(m_Charset, 0), &cs, TCI_SRCCHARSET) )
m_CodePage = cs.ciACP;
else
m_CodePage = CP_ACP;
}
else
{
TEXTMETRIC tm;
HDC hDC;
HFONT hFontOrig;
hDC = GetDC(NULL);
hFontOrig = (HFONT)SelectObject(hDC, m_hfont);
GetTextMetrics (hDC, &tm);
if ( TranslateCharsetInfo ((DWORD *)(DWORD_PTR)MAKELONG(tm.tmCharSet, 0), &cs, TCI_SRCCHARSET) )
m_CodePage = cs.ciACP;
else
m_CodePage = CP_ACP;
m_Charset = tm.tmCharSet;
SelectObject(hDC, hFontOrig);
ReleaseDC(NULL, hDC);
}
_Module.SetCodePage(m_CodePage);
return S_OK;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::SetActionData
//=--------------------------------------------------------------------------=
//
void CHtmlHelpControl::SetActionData(PCSTR psz)
{
m_pszActionData = StrChr(psz, ',');
if (m_pszActionData) {
m_pszActionData = lcStrDup(FirstNonSpace(m_pszActionData + 1));
}
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::ProcessPadding
//=--------------------------------------------------------------------------=
//
void CHtmlHelpControl::ProcessPadding(PCSTR pszPad)
{
if (IsEmptyString(pszPad))
return;
PCSTR psz = stristr(pszPad, txtHPad);
if (psz) {
psz = FirstNonSpace(psz + strlen(txtHPad));
m_hpadding = Atoi(psz);
}
psz = stristr(pszPad, txtVPad);
if (psz) {
psz = FirstNonSpace(psz + strlen(txtVPad));
m_vpadding = Atoi(psz);
}
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::SaveTextState
//=--------------------------------------------------------------------------=
// saves properties using IPropertyBag.
//
// Parameters:
// IPropertyBag * - [in] stream to write to.
// fWriteDefault - [in] ?
//
// Output:
// HRESULT
//
// Notes:
//
STDMETHODIMP CHtmlHelpControl::SaveTextState(IPropertyBag *pPropertyBag, BOOL fWriteDefault)
{
// Save the state of various properties.
WCHAR uniBufProperty[MAX_URL];
WCHAR uniBufValue[MAX_URL];
VARIANT v;
char szBuf[20];
char szTmp[MAX_URL];
int iItem = 1;
HRESULT hr = S_OK;
CHECK_POINTER(pPropertyBag);
//
// Remember the action data. i.e. ACT_KLINK, ACT_SAMPLE, ect.
//
MultiByteToWideChar(CP_ACP, 0, m_szRawAction, -1, uniBufValue, MAX_URL); // Value.
v.vt = VT_BSTR;
v.bstrVal = SysAllocString(uniBufValue);
pPropertyBag->Write(txtwCommand, &v);
VariantClear(&v);
//
// Now remember all the parameters we put into the CTable...
//
while ( m_ptblItems && m_ptblItems->GetString(szTmp, iItem) )
{
wsprintf(szBuf, txtItem, iItem++);
MultiByteToWideChar(CP_ACP, 0, szBuf, -1, uniBufProperty, MAX_URL); // Property.
MultiByteToWideChar(CP_ACP, 0, szTmp, -1, uniBufValue, MAX_URL); // Value.
v.vt = VT_BSTR;
v.bstrVal = SysAllocString(uniBufValue);
pPropertyBag->Write(uniBufProperty, &v);
VariantClear(&v);
}
//
// Lastly, restore the button text and font.
//
if ( m_pwszButtonText )
{
v.vt = VT_BSTR;
v.bstrVal = SysAllocString(m_pwszButtonText);
pPropertyBag->Write(txtwButton, &v);
VariantClear(&v);
}
if ( m_szFontSpec[0] )
{
MultiByteToWideChar(CP_ACP, 0, m_szFontSpec, -1, uniBufValue, MAX_URL); // Value.
v.vt = VT_BSTR;
v.bstrVal = SysAllocString(uniBufValue);
pPropertyBag->Write(txtwFont, &v);
VariantClear(&v);
}
return hr;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::LoadBinaryState
//=--------------------------------------------------------------------------=
// loads in our binary state using streams.
//
// Parameters:
// IStream * - [in] stream to write to.
//
// Output:
// HRESULT
//
// Notes:
//
STDMETHODIMP CHtmlHelpControl::LoadBinaryState(IStream *pStream)
{
DWORD sh;
HRESULT hr;
// first read in the streamhdr, and make sure we like what we're getting
hr = pStream->Read(&sh, sizeof(sh), NULL);
RETURN_ON_FAILURE(hr);
// sanity check
if (sh != STREAMHDR_MAGIC)
return E_UNEXPECTED;
hr = pStream->Read(&(m_state.endDate), sizeof(m_state.endDate), NULL);
return(SetBmpPath(pStream));
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::SaveBinaryState
//=--------------------------------------------------------------------------=
//
STDMETHODIMP CHtmlHelpControl::SaveBinaryState(IStream *pStream)
{
return S_OK;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::SetClientSite [IOleObject]
//=--------------------------------------------------------------------------=
// informs the embedded object [control] of it's client site [display
// location] within it's container
//
// Parameters:
// IOleClientSite * - [in] pointer to client site.
//
// Output:
// HRESULT - S_OK, E_UNEXPECTED
STDMETHODIMP CHtmlHelpControl::SetClientSite(IOleClientSite* pClientSite)
{
// Call the base class implementation first.
HRESULT hr = CInternetControl::SetClientSite(pClientSite);
LPSERVICEPROVIDER pISP;
if (m_pClientSite != NULL)
{
hr = m_pClientSite->QueryInterface(IID_IServiceProvider, (LPVOID*)&pISP);
if (SUCCEEDED(hr))
{
LPDISPATCH pIEDisp = NULL;
hr = pISP->QueryService(IID_IWebBrowserApp, IID_IDispatch, (LPVOID*)&pIEDisp);
if (SUCCEEDED(hr))
{
m_pWebBrowserApp = new IWebBrowserAppImpl(pIEDisp);
}
#ifdef _DEBUG
if (FAILED(hr))
OutputDebugString("Failed to get a pointer to IE's IDispatch\n");
#endif
pISP->Release();
return S_OK; // It's ok to fail the IWebBrowserApp wireing when we're printing.
}
}
return hr;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::OnDraw
//=--------------------------------------------------------------------------=
//
// Parameters:
// DWORD - [in] drawing aspect
// HDC - [in] HDC to draw to
// LPCRECTL - [in] rect we're drawing to
// LPCRECTL - [in] window extent and origin for meta-files
// HDC - [in] HIC for target device
// BOOL - [in] can we optimize dc handling?
HRESULT CHtmlHelpControl::OnDraw(DWORD dvaaspect, HDC hdcDraw,
LPCRECTL prcBounds, LPCRECTL prcWBounds, HDC hicTargetDevice,
BOOL fOptimize)
{
if (DesignMode()) {
HBRUSH hBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
FillRect(hdcDraw, (LPRECT)prcBounds, hBrush);
return S_OK;
}
if (prcWBounds != NULL) { // printing to a metafile ?
DBWIN("Metafile Printing not currently supported. <mikecole>");
return S_OK;
}
// Are we printing to a printer DC ?
//
if ( GetDeviceCaps(hdcDraw, TECHNOLOGY) == DT_RASPRINTER )
{
// On IE4 we have to do the printing ourselves.
//
if ( m_imgType != IMG_BITMAP && m_pwszButtonText && *m_pwszButtonText )
{
HFONT hfont;
if ( m_szFontSpec[0] )
hfont = CreateUserFont(m_szFontSpec, NULL, hdcDraw, m_Charset);
else
hfont = _Resource.DefaultPrinterFont(hdcDraw);
HFONT hfontOld = (HFONT) SelectObject(hdcDraw, hfont);
IntlExtTextOutW(hdcDraw, prcBounds->left, prcBounds->top, 0, NULL, m_pwszButtonText, lstrlenW(m_pwszButtonText), NULL);
SelectObject(hdcDraw, hfontOld);
DeleteObject(hfont);
}
return S_OK;
}
switch(m_imgType) {
case IMG_CHILD_WINDOW:
return S_OK; // no background to redraw
case IMG_TEXT:
case IMG_BUTTON:
return S_OK;
}
if ( (m_idBitmap == 0 || m_idBitmap == -1) && m_pszBitmap == NULL)
{
SIZEL szl;
szl.cx = 0;
szl.cy = 0;
SetControlSize(&szl);
}
else
{
HDC hdcTemp = CreateCompatibleDC(hdcDraw);
HBITMAP hBitmap = LoadBitmap(_Module.GetResourceInstance(), "shortcut");
HBITMAP hOld = (HBITMAP) SelectObject(hdcTemp, hBitmap);
BITMAP bm;
GetObject(hBitmap, sizeof(BITMAP), (LPSTR) &bm);
POINT ptSize;
ptSize.x = bm.bmWidth; // Get width of bitmap
ptSize.y = bm.bmHeight; // Get height of bitmap
DPtoLP(hdcTemp, &ptSize, 1); // Convert from device
// to logical points
SIZEL szl;
szl.cx = bm.bmWidth;
szl.cy = bm.bmHeight;
SetControlSize(&szl);
SelectObject(hdcTemp,hOld);
DeleteDC( hdcTemp );
DeleteObject(hBitmap);
}
return S_OK;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::WindowProc
//=--------------------------------------------------------------------------=
//
LRESULT CHtmlHelpControl::WindowProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg) {
case WM_ERASEBKGND:
{
// shanemc 7883: Support non-transparent index
if (ACT_INDEX == m_action && m_hbrBackGround != NULL) {
RECT rc;
GetClipBox((HDC) wParam, &rc);
FillRect((HDC) wParam, &rc, m_hbrBackGround);
return TRUE;
}
// end shanemc 7883
// Make the parent draw our background.
/*
* REVIEW: We could take advantage of this by having a flag
* set that would grab the pixel in the upper left corner and
* convert that into a background brush (m_hbrBackGround) that
* all of our transparent controls would use. That would save
* them from having to specify the background color in the
* object tag.
*/
HWND hwndParent = GetParent(m_hwnd);
if (hwndParent) {
// Adjust the origin so the parent paints in the right place
POINT pt;
ZERO_STRUCTURE(pt);
MapWindowPoints(m_hwnd, hwndParent, &pt, 1);
OffsetWindowOrgEx((HDC) wParam, pt.x, pt.y, &pt);
LRESULT lres = SendMessage(hwndParent, msg, wParam, lParam);
SetWindowOrgEx((HDC) wParam, pt.x, pt.y, NULL);
if (lres)
return lres;
}
}
break;
case WM_WINDOWPOSCHANGING: // bug HH 4281
RECT rect;
GetWindowRect(m_hwnd, &rect);
if ( memcmp((void*)&m_rect, (void*)&rect, sizeof(RECT)) != 0 )
InvalidateRect(m_hwnd, NULL, WM_ERASEBKGND);
m_rect = rect;
break;
case WM_CTLCOLORBTN:
if (m_hbrBackGround && m_hwndDisplayButton != (HWND) lParam)
return (LRESULT) m_hbrBackGround;
break;
case WM_CTLCOLOREDIT:
if (m_hbrBackGround && m_ptoc && IsValidWindow(m_ptoc->m_hwndTree))
return (LRESULT) m_hbrBackGround;
break;
case WM_CTLCOLORSTATIC:
if (m_hbrBackGround) {
// shanemc 7883 - Set the background color if Index
if (ACT_INDEX == m_action) {
SetBkColor((HDC)wParam, m_clrFontHover);
// Note hack of using FontHover color which to hold BkColor.
// FontHover color isn't used by Index and I don't want to change
// hhctrl.h if I can avoid it.
// shanemc 7924
// Setting the background color apparently changes the system's
// default foreground color to be black instead of window text color.
// This breaks readability. Unfortunately, we don't really know (at this
// point) whether the caller asked for the button background color or some
// other specific color. But we do know that Millennium will always ask
// for button face, so we'll do the following:
// 1) Assume btnface is the background color.
// 2) Assume btntext is the best foreground color.
// 3) Confirm that the background color isn't the same as the
// button text color
// 4) If they are the same, use windowtext, black, or white
// (whichever doesn't match)
COLORREF clrText = GetSysColor(COLOR_BTNTEXT);
if (clrText == m_clrFontHover) {
clrText = GetSysColor(COLOR_WINDOWTEXT);
if (clrText == m_clrFontHover) {
clrText = RGB(0,0,0);
}
if (clrText == m_clrFontHover) {
clrText = RGB(255,255,255);
}
}
SetTextColor((HDC)wParam, clrText);
// end shanemc 7924
}
// end shanemc 7883
return (LRESULT) m_hbrBackGround;
}
break;
case WMP_AUTHOR_MSG:
AuthorMsg((UINT)wParam, (PCSTR) lParam);
lcFree((void*) lParam);
return 0;
case WMP_USER_MSG:
if (lParam) {
char szMsg[512];
wsprintf(szMsg, GetStringResource((int)wParam), (PCSTR) lParam);
ModalDialog(TRUE);
MessageBox(GetParent(m_hwnd), szMsg, "", MB_OK | MB_ICONHAND);
ModalDialog(FALSE);
lcFree((void*) lParam);
}
else {
ModalDialog(TRUE);
MessageBox(GetParent(m_hwnd), GetStringResource((int)wParam), "",
MB_OK | MB_ICONHAND);
ModalDialog(FALSE);
}
return 0;
case WM_NOTIFY:
if (m_action == ACT_CONTENTS)
return m_ptoc->TreeViewMsg((NM_TREEVIEW*) lParam);
else if (m_action == ACT_INDEX)
{
if ( wParam == IDC_KWD_VLIST )
m_pindex->OnVKListNotify((NMHDR*)lParam);
return 0;
}
break;
case WM_DRAWITEM:
if (m_imgType == IMG_TEXT) {
OnDrawStaticText((DRAWITEMSTRUCT*) lParam);
break;
}
switch (m_action) {
case ACT_INDEX:
// m_pindex->OnDrawItem(wParam, (LPDRAWITEMSTRUCT) lParam);
return 0;
}
break;
case WM_SETFOCUS:
HWND hWndButton;
if ( m_fButton && (hWndButton = ::GetWindow(m_hwnd, GW_CHILD)) )
{
::SetFocus(hWndButton);
return 0;
}
else if ( (m_action == ACT_INDEX) && m_pindex)
{
m_pindex->SetDefaultFocus();
return 0;
}
break;
case WM_COMMAND:
// BN_CLICKED and STN_CLICKED have the same value
switch (HIWORD(wParam))
{
case BN_SETFOCUS:
::SendMessage((HWND)lParam, BM_SETSTYLE, (BS_DEFPUSHBUTTON | BS_NOTIFY), 1L);
return 0;
case BN_KILLFOCUS:
::SendMessage((HWND)lParam, BM_SETSTYLE, (BS_PUSHBUTTON | BS_NOTIFY), 1L);
return 0;
case BN_CLICKED:
case BN_DOUBLECLICKED:
if ( m_fButton && LOWORD(wParam) < ID_VIEW_ENTRY )
{
#ifdef _DEBUG
if (LOWORD(wParam) == ID_VIEW_MEMORY) {
OnReportMemoryUsage();
return 0;
}
#endif
if (LOWORD(wParam) >= IDM_RELATED_TOPIC && LOWORD(wParam) <= IDM_RELATED_TOPIC + 100) {
OnRelatedCommand(LOWORD(wParam));
}
else {
OnClick();
}
return 0;
}
// Hmmm, I wonder if a fall through is intended here ?
default:
switch (m_action) {
case ACT_INDEX:
return m_pindex->OnCommand(m_hwnd, LOWORD(wParam), HIWORD(wParam), lParam);
case ACT_CONTENTS:
{
LRESULT lr = m_ptoc->OnCommand(m_hwnd, LOWORD(wParam), HIWORD(wParam), lParam);
#if 0
// Since SendEvent() has to be a member of CHtmlHelpControl, it's called
// at this point.
SendEvent();
#endif
return lr;
}
case ACT_RELATED_TOPICS:
case ACT_ALINK:
case ACT_KLINK:
OnRelatedCommand(LOWORD(wParam));
return 0;
}
}
break;
case WM_CONTEXTMENU:
switch (m_action) {
case ACT_RELATED_TOPICS:
case ACT_INDEX:
if (IsHelpAuthor(GetParent(m_hwnd))) {
HMENU hmenu = CreatePopupMenu();
if (!hmenu)
break;
if (m_action == ACT_RELATED_TOPICS) {
CStr csz;
for (int i = 1; i <= m_pSiteMap->Count(); i++) {
csz = GetStringResource(IDS_VIEW_RELATED);
csz += m_pSiteMap->GetSiteMapEntry(i)->pszText;
HxAppendMenu(hmenu, MF_STRING, ID_VIEW_ENTRY + i,
csz);
}
}
else {
HxAppendMenu(hmenu, MF_STRING, ID_VIEW_ENTRY,
pGetDllStringResource(IDS_VIEW_ENTRY));
}
#ifdef _DEBUG
HxAppendMenu(hmenu, MF_STRING, ID_VIEW_MEMORY,
"Debug: memory usage...");
#endif
POINT pt;
GetCursorPos(&pt);
TrackPopupMenu(hmenu,
TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON,
pt.x, pt.y, 0, m_hwnd, NULL);
}
break;
}
break;
}
return OcxDefWindowProc(msg, wParam, lParam);
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::SetObjectRects
//=--------------------------------------------------------------------------=
//
STDMETHODIMP CHtmlHelpControl::SetObjectRects(LPCRECT prcPos, LPCRECT prcClip)
{
BOOL fRemoveWindowRgn;
// verify our information
// This assertion doesn't seem valid because the container (IE 3) never
// calls SetExtent().
// ASSERT_COMMENT(m_Size.cx == (prcPos->right - prcPos->left) && m_Size.cy == (prcPos->bottom - prcPos->top), "Somebody called SetObjectRects without first setting the extent");
/*
* Move our window to the new location and handle clipping. Not
* applicable for windowless controls, since the container will be
* responsible for all clipping.
*/
if (m_hwnd) {
fRemoveWindowRgn = m_fUsingWindowRgn;
if (prcClip) {
// the container wants us to clip, so figure out if we really
// need to
RECT rcIXect;
if (IntersectRect(&rcIXect, prcPos, prcClip)) {
if (!EqualRect(&rcIXect, prcPos)) {
OffsetRect(&rcIXect, -(prcPos->left), -(prcPos->top));
SetWindowRgn(m_hwnd, CreateRectRgnIndirect(&rcIXect), TRUE);
m_fUsingWindowRgn = TRUE;
fRemoveWindowRgn = FALSE;
}
}
}
if (fRemoveWindowRgn) {
SetWindowRgn(m_hwnd, NULL, TRUE);
m_fUsingWindowRgn = FALSE;
}
// set our control's location and size
// [people for whom zooming is important should set that up here]
// if (!EqualRect(prcPos, &m_rcLocation)) {
m_Size.cx = RECT_WIDTH(prcPos);
m_Size.cy = RECT_HEIGHT(prcPos);
SetWindowPos(m_hwnd, NULL, prcPos->left, prcPos->top, m_Size.cx, m_Size.cy, SWP_NOZORDER | SWP_NOACTIVATE);
CopyRect(&m_rcLocation, prcPos);
switch (m_action) {
case ACT_CONTENTS:
if(m_ptoc)
m_ptoc->ResizeWindow();
return S_OK;
case ACT_INDEX:
if(m_pindex)
m_pindex->ResizeWindow();
// OnSizeIndex(&m_rcLocation);
return S_OK;
}
return S_OK;
// }
}
// save out our current location. windowless controls want this more
// then windowed ones do, but everybody can have it just in case
// BUGBUG: 20-Apr-1997 [ralphw] why do we care about this for
// windowless controls
CopyRect(&m_rcLocation, prcPos);
return S_OK;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::JumpToUrl
//=--------------------------------------------------------------------------=
//
void CHtmlHelpControl::JumpToUrl(SITEMAP_ENTRY* pSiteMapEntry, CSiteMap* pSiteMap, SITE_ENTRY_URL* pUrl)
{
// REVIEW: we should make this an array of m_hwndHelp to handle more then
// on help window open at once.
HWND hwnd = ::JumpToUrl(m_pUnkOuter, m_hwndParent, pSiteMapEntry, m_pInfoType, pSiteMap, pUrl);
// if (hwnd) {
// if (m_hwndHelp && hwnd != m_hwndHelp)
// DestroyWindow(m_hwndHelp);
// m_hwndHelp = hwnd;
// }
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::OnSpecialKey
//
// Function is called from COleControl::TranslateAccelerator() and should
// return TRUE if the key has been handled.
//
//=--------------------------------------------------------------------------=
BOOL CHtmlHelpControl::OnSpecialKey(LPMSG pmsg)
{
static int sDisplayAccel = 'd';
static int sEditAccel = 'w';
static BOOL sbIsAccelsSet = FALSE;
DBWIN("OnSpecialKey");
// Get the current state of the SHIFT key.
BOOL fShiftDown = (GetKeyState(VK_SHIFT) < 0);
if (m_action == ACT_CONTENTS) {
if (GetFocus() == m_hwnd)
::SetFocus(m_ptoc->m_hwndTree);
// If the user pressed the Tab key, let the container
// process it so it can set focus to the appropriate object.
if (pmsg->wParam == VK_TAB &&
(pmsg->message == WM_KEYUP || pmsg->message == WM_KEYDOWN))
return FALSE;
SendMessage(m_ptoc->m_hwndTree, pmsg->message, pmsg->wParam, pmsg->lParam);
return TRUE;
}
if (m_action == ACT_INDEX) {
// The main control window shouldn't have the focus; so if it
// does, move it to the appropriate control depending on the
// state of the SHIFT key. (Tabbing to the control on an HTML
// page will set the focus to the main window.)
if (pmsg->message == WM_SYSKEYDOWN)
{
if (sbIsAccelsSet == FALSE)
{
sbIsAccelsSet = TRUE;
PCSTR psz = StrChr(GetStringResource(IDS_ENGLISH_DISPLAY), '&');
if (psz)
sDisplayAccel = ToLower(psz[1]);
psz = StrChr(GetStringResource(IDS_TYPE_KEYWORD), '&');
if (psz)
sEditAccel = ToLower(psz[1]);
}
if (ToLower((char)pmsg->wParam) == sEditAccel)
{
::SetFocus(m_pindex->m_hwndEditBox);
return TRUE;
}
else if (ToLower((char)pmsg->wParam) == sDisplayAccel)
{
::SendMessage(GetParent(m_pindex->m_hwndDisplayButton), WM_COMMAND, MAKELONG(IDBTN_DISPLAY, BN_CLICKED), (LPARAM)m_pindex->m_hwndDisplayButton);
return TRUE;
}
}
if (GetFocus() == m_hwnd){
if (fShiftDown)
::SetFocus(m_pindex->m_hwndDisplayButton);
else
::SetFocus(m_pindex->m_hwndEditBox);
return TRUE;
}
if (GetFocus() == m_pindex->m_hwndEditBox) {
if ((pmsg->message == WM_KEYUP || pmsg->message == WM_KEYDOWN) &&
pmsg->wParam == VK_TAB) {
if (fShiftDown) {
return FALSE; // Let the container handle this.
}
else {
// The focus only needs to be set once on WM_KEYDOWN.
if (pmsg->message == WM_KEYDOWN)
::SetFocus(m_pindex->m_hwndListBox);
return TRUE;
}
}
else
{
if ( pmsg->message == WM_KEYDOWN && (pmsg->wParam >= VK_PRIOR && pmsg->wParam <= VK_DOWN))
{
SendMessage(m_pindex->m_hwndEditBox, pmsg->message, pmsg->wParam, pmsg->lParam);
return TRUE;
}
else
return FALSE;
}
}
else if (GetFocus() == m_pindex->m_hwndListBox) {
if ((pmsg->message == WM_KEYUP || pmsg->message == WM_KEYDOWN) &&
pmsg->wParam == VK_TAB) {
if (fShiftDown) {
if (pmsg->message == WM_KEYUP)
::SetFocus(m_pindex->m_hwndEditBox);
return TRUE;
}
else {
if (pmsg->message == WM_KEYDOWN)
::SetFocus(m_pindex->m_hwndDisplayButton);
return TRUE;
}
}
else {
// DonDr - Return TRUE instead of !SendMessage because it was causing double
// scrolls if there was no scroll bar in IE.
SendMessage(m_pindex->m_hwndListBox, pmsg->message, pmsg->wParam, pmsg->lParam);
return TRUE;
}
}
else if (GetFocus() == m_pindex->m_hwndDisplayButton) {
if ((pmsg->message == WM_KEYUP || pmsg->message == WM_KEYDOWN) &&
pmsg->wParam == VK_TAB) {
if (fShiftDown) {
if (pmsg->message == WM_KEYDOWN)
::SetFocus(m_pindex->m_hwndListBox);
return TRUE;
}
else {
return FALSE;
}
}
}
}
return FALSE;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::OnSetExtent
//=--------------------------------------------------------------------------=
//
BOOL CHtmlHelpControl::OnSetExtent(const SIZE* pSize)
{
BOOL bRet = TRUE;
if ( m_fButton )
{
if ( IsValidWindow(m_hwndDisplayButton) )
{
m_Size.cx = RECT_WIDTH(m_rcButton);
m_Size.cy = RECT_HEIGHT(m_rcButton);
bRet = FALSE;
}
else if ( m_pwszButtonText && m_pwszButtonText[0] )
{
// Compute the rect for the text. NOTE: This code will only be entered at print time.
// for buttons that contain text.
//
HDC hDC;
SIZE Size;
if ( (hDC = GetDC(NULL)) )
{
HFONT hfontOld = (HFONT) SelectObject(hDC, m_hfont);
IntlGetTextExtentPoint32W(hDC, m_pwszButtonText, lstrlenW(m_pwszButtonText), &Size);
SelectObject(hDC, hfontOld);
m_Size.cx = Size.cx + 2; // The two pel "fudge factor" is a lazy way to avoid potential ABC spacing problems.
m_Size.cy = Size.cy + 2;
if ( m_imgType == IMG_BUTTON )
m_Size.cy += 12;
bRet = FALSE;
ReleaseDC(NULL, hDC);
}
}
}
return bRet;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::get_Image
//=--------------------------------------------------------------------------=
//
STDMETHODIMP CHtmlHelpControl::get_Image(BSTR* path)
{
CHECK_POINTER(path);
BSTR * pbstrPath = path;
*pbstrPath = (m_state.bmpPath && *(m_state.bmpPath)) ? BSTRFROMANSI(m_state.bmpPath) : SysAllocString(L"");
return (*pbstrPath) ? S_OK : E_OUTOFMEMORY;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::put_Image
//=--------------------------------------------------------------------------=
//
STDMETHODIMP CHtmlHelpControl::put_Image(BSTR path)
{
char * tmp = 0;
if (m_state.bmpPath)
delete m_state.bmpPath;
int len = wcslen(path);
HRESULT hr;
tmp = new char[len+1];
if (!tmp) {
FAIL("No memory");
hr = E_OUTOFMEMORY;
}
else if (WideCharToMultiByte(CP_ACP, 0, path, len, tmp, len, NULL, NULL) == 0)
return E_FAIL;
*(tmp + len) = '\0';
// if it hasn't changed, don't waste any time.
if(m_state.bmpPath)
{
if (!strcmp(m_state.bmpPath, tmp))
return S_OK;
}
if (m_state.bmpPath)
delete m_state.bmpPath;
m_state.bmpPath = tmp;
UpdateImage();
return(hr);
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::Click
//=--------------------------------------------------------------------------=
//
// This is called from scripting in the browser
//
STDMETHODIMP CHtmlHelpControl::Click()
{
OnClick();
return S_OK;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::HHClick
//=--------------------------------------------------------------------------=
//
// HHClick is a duplicate of Click() because in IE 4, VBScript decided to
// implement a Click command that overrides the OCX.
//
STDMETHODIMP CHtmlHelpControl::HHClick()
{
OnClick();
return S_OK;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::syncURL
//=--------------------------------------------------------------------------=
//
STDMETHODIMP CHtmlHelpControl::syncURL(BSTR pszUrl)
{
if (pszUrl != NULL && m_ptoc)
{
CStr cszUrl((WCHAR*) pszUrl);
m_ptoc->Synchronize(cszUrl);
}
return S_OK;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::TCard
//=--------------------------------------------------------------------------=
//
STDMETHODIMP CHtmlHelpControl::TCard(WPARAM wParam, LPARAM lParam)
{
HWND hwndParent = FindTopLevelWindow(GetParent(m_hwnd));
if (hwndParent) {
SendMessage(hwndParent, WM_TCARD, wParam, lParam);
return S_OK;
}
else
return S_FALSE;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::Print
//=--------------------------------------------------------------------------=
//
STDMETHODIMP CHtmlHelpControl::Print()
{
HRESULT hr = S_OK;
if (m_pWebBrowserApp->m_lpDispatch != NULL &&
m_ptoc != NULL)
{
PostMessage(m_hwnd, WM_COMMAND, ID_PRINT, 0);
#if 0
int action = PRINT_CUR_TOPIC;
HTREEITEM hitem = TreeView_GetSelection(m_ptoc->m_hwndTree);
if (hitem) {
CPrint prt(m_hwndParent);
prt.SetAction(action);
if (!prt.DoModal())
return hr;
action = prt.GetAction();
}
PrintTopics(action, m_ptoc, m_pWebBrowserApp);
#endif
}
else
hr = E_FAIL;
return hr;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::TextPopup
//=--------------------------------------------------------------------------=
//
STDMETHODIMP CHtmlHelpControl::TextPopup(BSTR pszText, BSTR pszFont,
int horzMargins, int vertMargins, COLORREF clrForeground, COLORREF clrBackground)
{
if (pszText != NULL) {
HH_POPUP popup;
popup.cbStruct = sizeof(HH_POPUP);
popup.hinst = NULL; // This should be zero if idString is zero.
popup.idString = 0;
popup.pszText = (PCSTR) pszText;
GetCursorPos(&popup.pt);
popup.clrForeground = clrForeground;
popup.clrBackground = clrBackground;
popup.rcMargins.left = horzMargins;
popup.rcMargins.top = vertMargins;
popup.rcMargins.right = horzMargins;
popup.rcMargins.bottom = vertMargins;
popup.pszFont = (PCSTR) pszFont;
xHtmlHelpW(FindTopLevelWindow(GetParent(m_hwnd)), NULL,
HH_DISPLAY_TEXT_POPUP, (DWORD_PTR) &popup);
return S_OK;
}
else
return S_FALSE;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::SetBmpPath
//=--------------------------------------------------------------------------=
//
HRESULT CHtmlHelpControl::SetBmpPath(IStream * strm)
{
CHECK_POINTER(strm);
char * tmp = 0;
if (m_state.bmpPath)
delete m_state.bmpPath;
DWORD dw;
HRESULT hr = strm->Read(&dw, sizeof(dw), 0);
if (SUCCEEDED(hr)) {
if (!dw) {
hr = S_OK;
}
else {
tmp = new char[dw+1];
if (!tmp) {
FAIL("No memory");
hr = E_OUTOFMEMORY;
}
else {
hr = strm->Read(tmp, dw + 1, 0);
}
}
}
// if it hasn't changed, don't waste any time.
if ((!tmp && !m_state.bmpPath) || !strcmp(m_state.bmpPath, tmp))
return S_OK;
if (m_state.bmpPath)
delete m_state.bmpPath;
m_state.bmpPath = tmp;
UpdateImage();
return(hr);
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::UpdateImage
//=--------------------------------------------------------------------------=
//
HRESULT CHtmlHelpControl::UpdateImage()
{
if (!m_hwnd)
return(S_OK);
if (!m_state.bmpPath)
return(S_OK);
FireReadyStateChange(READYSTATE_INTERACTIVE);
return(SetupDownload(OLESTRFROMANSI(m_state.bmpPath), DISPID_BMPPATH));
return S_OK;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::OnData
//=--------------------------------------------------------------------------=
//
HRESULT CHtmlHelpControl::OnData(DISPID propId, DWORD grfBSCF,
IStream * strm, DWORD dwSize)
{
HRESULT hr = NOERROR;
switch(m_readystate) {
case bdsNoBitsYet:
#if 0
if (dwSize >= sizeof(BITMAPFILEHEADER)) {
if( m_dibFile )
delete m_dibFile;
m_dibFile = new CDibFile;
if (!m_dibFile) {
hr = E_OUTOFMEMORY;
break;
}
hr = m_dibFile->GetFileHeader(strm);
if (FAILED(hr))
break;
m_readystate = bdsGotFileHeader;
// now FALL THRU!
}
else
#endif
break;
case bdsGotFileHeader:
#if 0
if (dwSize >= (m_dibFile->HeaderSize() + sizeof(BITMAPFILEHEADER)))
{
if (m_dibFile)
hr = m_dibFile->GetInfoHeader(strm);
else
hr = E_OUTOFMEMORY;
if (FAILED(hr))
break;
if (m_dib)
delete m_dib;
m_dib = new CDibSection;
if (!m_dib) {
hr = E_OUTOFMEMORY;
break;
}
m_dib->Setup(m_dc);
hr = m_dib->Create(*m_dibFile);
if (FAILED(hr))
break;
m_dib->ImageSize(m_dibFile->CalcImageSize());
m_readystate = bdsGotBitmapInfo;
// FALL THRU!
}
else
#endif
break;
case bdsGotBitmapInfo:
#if 0
SIZEL sz;
m_dib->GetSize(sz);
SetControlSize(&sz);
m_oldSize = (m_dibFile->HeaderSize() + sizeof(BITMAPFILEHEADER));
m_readystate = bdsGettingBits;
#endif
// FALL THRU
case bdsGettingBits:
#if 0
if (dwSize > m_oldSize) {
hr = m_dib->ReadFrom(strm, dwSize - m_oldSize);
if (FAILED(hr))
break;
::RealizePalette(m_dc);
m_dib->PaintTo(m_dc);
m_oldSize = dwSize;
}
if (grfBSCF & BSCF_LASTDATANOTIFICATION)
m_readystate = bdsBitsAreDone;
else
#endif
break;
case bdsBitsAreDone:
#if 0
m_readystate = bdsNoBitsYet;
FireReadyStateChange(READYSTATE_COMPLETE);
InvalidateControl(0);
#endif
break;
}
return(hr);
}
#ifndef PPGS
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::DoVerb
//=--------------------------------------------------------------------------=
//
STDMETHODIMP CHtmlHelpControl::DoVerb
(
LONG lVerb,
LPMSG pMsg,
IOleClientSite *pActiveSite,
LONG lIndex,
HWND hwndParent,
LPCRECT prcPosRect
)
{
switch (lVerb) {
case OLEIVERB_PRIMARY:
case CTLIVERB_PROPERTIES:
case OLEIVERB_PROPERTIES:
case OLEIVERB_OPEN:
case OLEIVERB_HIDE:
return S_OK;
#ifdef _DEBUG
case OLEIVERB_INPLACEACTIVATE:
case OLEIVERB_UIACTIVATE:
//DBWIN("ACTIVATE");
break;
#endif
}
return COleControl::DoVerb(lVerb, pMsg, pActiveSite, lIndex, hwndParent, prcPosRect);
}
#endif
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::InternalQueryInterface
//=--------------------------------------------------------------------------=
// qi for things only we support.
//
// Parameters:
// Parameters:
// REFIID - [in] interface they want
// void ** - [out] where they want to put the resulting object ptr.
//
// Output:
// HRESULT - S_OK, E_NOINTERFACE
HRESULT CHtmlHelpControl::InternalQueryInterface(REFIID riid, void **ppvObjOut)
{
IUnknown *pUnk;
if (!ppvObjOut)
return E_INVALIDARG;
*ppvObjOut = NULL;
/*
* TODO: if you want to support any additional interrfaces, then you
* should indicate that here. never forget to call CInternetControl's
* version in the case where you don't support the given interface.
*/
if (DO_GUIDS_MATCH(riid, IID_IHHCtrl)) {
pUnk = (IUnknown *)(IHHCtrl *)this;
} else{
return CInternetControl::InternalQueryInterface(riid, ppvObjOut);
}
pUnk->AddRef();
*ppvObjOut = (void *)pUnk;
return S_OK;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::ConvertToCacheFile
//=--------------------------------------------------------------------------=
//
BOOL CHtmlHelpControl::ConvertToCacheFile(PCSTR pszSrc, PSTR pszDst)
{
CStr cszTmp;
PSTR psz = stristr(pszSrc, txtSysRoot);
if (psz) {
char szPath[MAX_PATH];
GetRegWindowsDirectory(szPath);
strcat(szPath, psz + strlen(txtSysRoot));
cszTmp = szPath;
pszSrc = (PCSTR) cszTmp.psz;
}
TryThatAgain:
PCSTR pszChmSep = strstr(pszSrc, txtDoubleColonSep);
if (pszChmSep) {
if (pszSrc != cszTmp.psz) {
cszTmp = pszSrc;
int offset = (int)(pszChmSep - pszSrc);
pszSrc = cszTmp;
pszChmSep = pszSrc + offset;
}
*(PSTR) pszChmSep = '\0'; // Remove the separator
HRESULT hr = URLDownloadToCacheFile(m_pUnkOuter, pszSrc, pszDst, MAX_PATH, 0, NULL);
if (!SUCCEEDED(hr)) {
CStr cszNew;
ModalDialog(TRUE);
BOOL fResult = FindThisFile(m_hwnd, pszSrc, &cszNew, TRUE);
ModalDialog(FALSE);
if (fResult) {
strcpy(pszDst, cszNew.psz);
*(PSTR) pszChmSep = ':'; // Put the separator back
strcat(pszDst, pszChmSep);
return TRUE;
}
}
else { // we downloaded it, or have a pointer to it
*(PSTR) pszChmSep = ':'; // Put the separator back
strcat(pszDst, pszChmSep);
return TRUE;
}
}
// BUGBUG: need to get the current ULR -- if this is a "mk:" URL, then
// we need to change the path before this will work
HRESULT hr = URLDownloadToCacheFile(m_pUnkOuter, pszSrc, pszDst, MAX_PATH, 0, NULL);
if (!SUCCEEDED(hr)) {
if (!StrChr(pszSrc, ':')) { // no protocol, check current .CHM file
HWND hwndParent = FindTopLevelWindow(GetParent(m_hwnd));
char szClass[256];
GetClassName(hwndParent, szClass, sizeof(szClass));
if (IsSamePrefix(szClass, txtHtmlHelpWindowClass, -2)) {
PCSTR pszFile = (PCSTR) SendMessage(hwndParent,
WMP_GET_CUR_FILE, 0, 0);
if (IsNonEmptyString(pszFile)) {
cszTmp = pszFile;
cszTmp += txtDoubleColonSep;
cszTmp += pszSrc;
pszSrc = cszTmp;
goto TryThatAgain;
}
}
}
return FALSE;
}
return TRUE;
}
//=--------------------------------------------------------------------------=
// CHtmlHelpControl::SendEvent
//=--------------------------------------------------------------------------=
//
HRESULT CHtmlHelpControl::SendEvent(LPCTSTR pszEventString)
{
if (!IsEmptyString(pszEventString)) {
CWStr cwsz(pszEventString); // convert to WIDE
BSTR bstr;
if ((bstr = SysAllocString(cwsz)) != NULL)
FireEvent(&::rgHHCtrlEvents[HHCtrlEvent_Click], bstr);
SysFreeString(bstr);
}
return S_OK;
}
//=--------------------------------------------------------------------------=
// GetHtmlHelpFrameWindow
//=--------------------------------------------------------------------------=
//
/*
This function gets the HWND for HTML Helps frame. HTML Help (CContainer) contains IE.
IE contains an AKLink ActiveX contain (CHtmlHelpControl). This function gives CHtmlHelpControl
a way to get to the HWND of its big daddy.
*/
HWND
CHtmlHelpControl::GetHtmlHelpFrameWindow()
{
HWND hWndReturn = NULL ;
if (m_pWebBrowserApp)
{
//--- Get a pointer to the container's IDispatch. This points to the IDispatch of CContainer (contain.cpp).
IDispatch* pDispatchContainer = m_pWebBrowserApp->GetContainer() ;
if (pDispatchContainer)
{
//--- Get the IOleWindow interface implemented by the container.
IOleWindow* pIOleWindow = NULL ;
HRESULT hr = pDispatchContainer->QueryInterface(IID_IOleWindow, (void**)&pIOleWindow) ;
if (SUCCEEDED(hr) && pIOleWindow)
{
//--- Get the window for this container.
HWND hWndTemp = NULL ;
hr = pIOleWindow->GetWindow(&hWndTemp) ;
if (SUCCEEDED(hr) && IsWindow(hWndTemp))
{
//--- We found the container window! Now, get the HTML Help Frame window from this.
CHHWinType* phh = FindHHWindowIndex(hWndTemp) ;
if (phh && IsWindow(phh->hwndHelp))
{
hWndReturn = phh->hwndHelp ;
}
}
// Clean up.
pIOleWindow->Release() ;
}
// Clean up some more.
pDispatchContainer->Release() ;
}
}
return hWndReturn ;
}