Click here to Skip to main content
15,893,588 members
Articles / Desktop Programming / MFC

Office 2000 Style Html Help Viewer

Rate me:
Please Sign up or sign in to vote.
4.45/5 (10 votes)
19 Feb 2001 139.5K   2K   45  
A free (source included) Html Help Viewer, in the style of the Office 2000 Help Viewer, including docking and auto-hide.
// Copyright (c) 1998-1999 KeyWorks Software. 

// This code is free; you can redistribute it and/or modify it as you see fit.
// In addition, you may also charge for any application using the CHHCtrl
// class, and are under no obligation to supply source code. 

// This software code is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

// Because no income is generated from this code, the author cannot spend
// copious amounts of time answering questions. Nevertheless, if you are stuck
// on an implementation problem, first check www.keyworks.net and if you can't
// find what you need there, send email to ralphw@exmsft.com who will answer
// based on whether he is in town, how many other questions have been
// submitted, etc.

// This code is not static. Additions and enhancements will be added to it over
// time and posted to www.keyworks.net (information types and scripting are
// currently under development). It is suggested that if you make changes, you
// subclass CHHCtrl rather then modifying this module directly. That way you
// can pick up an update and not break your existing code.

#include "stdafx.h"
#include "hhctrl.h"
#include "CIts.h"
#include "resource.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static const char THIS_FILE[] = __FILE__;
#endif

const int TB_PAD = 2;   // vertical padding between toolbar and HTML window

IMPLEMENT_DYNAMIC(CHHCtrl, CWnd)

BEGIN_MESSAGE_MAP(CHHCtrl, CWnd)
    //{{AFX_MSG_MAP(CHHCtrl)
    ON_WM_SIZE()
    ON_WM_PAINT()
    ON_WM_DESTROY()
    //}}AFX_MSG_MAP
    // Standard printing commands
    ON_COMMAND(ID_FILE_PRINT, OnFilePrint)
END_MESSAGE_MAP()

BEGIN_EVENTSINK_MAP(CHHCtrl, CWnd)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 102, OnStatusTextChange, VTS_BSTR)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 108, OnProgressChange, VTS_I4 VTS_I4)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 105, OnCommandStateChange, VTS_I4 VTS_BOOL)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 106, OnDownloadBegin, VTS_NONE)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 104, OnDownloadComplete, VTS_NONE)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 113, OnTitleChange, VTS_BSTR)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 252, NavigateComplete2, VTS_DISPATCH VTS_PVARIANT)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 250, BeforeNavigate2, VTS_DISPATCH VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PBOOL)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 112, OnPropertyChange, VTS_BSTR)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 251, OnNewWindow2, VTS_PDISPATCH VTS_PBOOL)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 259, DocumentComplete, VTS_DISPATCH VTS_PVARIANT)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 253, OnQuit, VTS_NONE)
    ON_EVENT(CHHCtrl, AFX_IDW_PANE_FIRST, 254, OnVisible, VTS_BOOL)
END_EVENTSINK_MAP()

CHHCtrl::CHHCtrl() : CWnd()
{
    m_pBrowserApp = NULL;
    m_paMapIds = NULL;
    m_pimg = NULL;
    m_pimgHot = NULL;
    m_pTB = NULL;
    m_mapHome = 0;
    m_idLastButton = HH_TB_CLOSE;
}

CHHCtrl::~CHHCtrl()
{
    if (m_pBrowserApp != NULL)
        m_pBrowserApp->Release();
    if (m_paMapIds)
        LocalFree((HLOCAL) m_paMapIds);
    if (m_pimg)
        delete m_pimg;
    if (m_pimgHot)
        delete m_pimgHot;
    if (m_pTB) 
        delete m_pTB;
    if (m_hWnd && IsWindow(m_hWnd))
        DestroyWindow();
}

void CHHCtrl::OnFilePrint()
{
    if (m_pBrowserApp != NULL) {
        LPDISPATCH lpDisp = GetHtmlDocument();

        if (lpDisp != NULL) {
            // the control will handle all printing UI

            LPOLECOMMANDTARGET lpTarget;
            if (SUCCEEDED(lpDisp->QueryInterface(IID_IOleCommandTarget,
                    (LPVOID*) &lpTarget))) {
                lpTarget->Exec(NULL, OLECMDID_PRINT, 0, NULL, NULL);
                lpTarget->Release();
            }
            lpDisp->Release();
        }
    }
}

#ifdef _DEBUG
void CHHCtrl::AssertValid() const
{
    CWnd::AssertValid();
}

void CHHCtrl::Dump(CDumpContext& dc) const
{
    CWnd::Dump(dc);
}
#endif //_DEBUG

void CHHCtrl::OnDestroy()
{
    if (m_pBrowserApp) {
        m_pBrowserApp->Release();
        m_pBrowserApp = NULL;
    }
}

void CHHCtrl::OnSize(UINT nType, int cx, int cy)
{
    CWnd::OnSize(nType, cx, cy);

    if (::IsWindow(m_hWnd)) {
        // need to push non-client borders out of the client area
        CRect rect;
        GetClientRect(rect);
        ::AdjustWindowRectEx(rect, GetStyle(), FALSE, WS_EX_CLIENTEDGE);
        SetWindowPos(NULL, rect.left, rect.top,
            rect.Width(), rect.Height(), SWP_NOACTIVATE | SWP_NOZORDER);
    }
}

void CHHCtrl::OnPaint()
{
    Default();
}

#define TB_HEIGHT   50
#define TB_IMAGE_CX 20

bool CHHCtrl::Create(CWnd* pParent, const RECT& rc, DWORD dwBtns, LPCTSTR pszHomeURL, bool fText)
{
    AfxEnableControlContainer();
    CRect rcClient(rc);
    if (IsBadReadPtr(pszHomeURL, sizeof(LPCTSTR)))
        m_mapHome = (int) pszHomeURL;
    else
        m_cszHome = pszHomeURL;

    if (dwBtns) {
        if (m_pTB) {
            delete m_pTB;
            delete m_pimg;
            delete m_pimgHot;
#ifdef _DEBUG
            m_pTB = NULL;
            m_pimg = NULL;
            m_pimgHot = NULL;
#endif
        }

        // BUGBUG: Need to calculate height based on system font
        rcClient.bottom = rcClient.top + TB_IMAGE_CX + (fText ? 23 : 8);
        ASSERT(!m_pTB);
        m_pTB = new CToolBarCtrl;
        if (!m_pTB->Create(WS_CHILD | WS_VISIBLE | 
                TBSTYLE_FLAT | TBSTYLE_TOOLTIPS |
                CCS_NODIVIDER | CCS_NORESIZE,
                rcClient, pParent, 1)) {
            TRACE0("Unable to create CToolBarCtrl");
            ASSERT(FALSE);
            delete m_pTB;
            return false;
        }
        ASSERT(!m_pimg);
        ASSERT(!m_pimgHot);
        m_pimg = new CImageList;
        m_pimgHot = new CImageList;
        m_pimg->Create(IDB_CHHCTRLG, TB_IMAGE_CX, 0, RGB(255,0,255));
        m_pimgHot->Create(IDB_CHHCTRL, TB_IMAGE_CX, 0, RGB(255,0,255));
        m_pTB->SetImageList(m_pimg);
        m_pTB->SetHotImageList(m_pimgHot);

        TBBUTTON tb[31];    // since we use bit flags, this is as high as you can go
        ASSERT((m_idLastButton - HH_TB_HOME) < 31);

        // Load the text for our buttons

        if (fText) {

            // Load the button string and change all pipe characters into null terminators

            TCHAR szBuf[512];
            if (LoadString(AfxGetInstanceHandle(), IDS_HHCTRL_TBSTRINGS, szBuf, sizeof(szBuf)) == 0) {
                TRACE0("Missing IDS_HHCTRL_TBSTRINGS resource string");
                ASSERT(FALSE);
                return false;
            }
            TCHAR* psz = szBuf;
            while ((psz = StrChr(psz, '|'))) 
                *psz++ =  '\0';
            m_pTB->AddStrings(szBuf);
        }

        // Now add the buttons we are going to use

        int iBtn = 0;
        for (int i = 0; i <= (m_idLastButton - HH_TB_HOME); i++) {
            if (dwBtns & (1 << i)) {

                // Special-case the Home button which requires a URL or map id

                if (i == 0 && !(pszHomeURL || m_mapHome))
                    continue;
                tb[iBtn].fsState = TBSTATE_ENABLED;
                tb[iBtn].fsStyle = TBSTYLE_BUTTON;
                tb[iBtn].dwData = 0;
                if (fText)
                    tb[iBtn].iString = i;
                else
                    tb[iBtn].iString = 0;
                tb[iBtn].iBitmap = i;
                tb[iBtn].idCommand = HH_TB_HOME + i;
                iBtn++;
            }
        }
        m_pTB->AddButtons(iBtn, tb);

        // Add some padding to the top of the window so there is a tiny bit of space
        // between the top of the button and the top of the host window.

        CRect rcBtn;
        m_pTB->GetWindowRect(&rcBtn);

        rcClient.top = rc.top + rcBtn.Height() + TB_PAD;
        rcClient.bottom = rc.bottom;
    }

    if (!CreateControl(CLSID_WebBrowser, NULL, 
            WS_VISIBLE | WS_CHILD, rcClient, pParent, AFX_IDW_PANE_FIRST)) {
        TRACE0("Unable to create control");
        return false;
    }

    LPUNKNOWN lpUnk = GetControlUnknown();
    HRESULT hr = lpUnk->QueryInterface(IID_IWebBrowser2, (void**) &m_pBrowserApp);
    if (FAILED(hr)) {
        m_pBrowserApp = NULL;
        TRACE0("Unable to create web browser");
        return false;
    }

    return true;
}

bool CHHCtrl::ReplaceControl(CWnd* pDlg, UINT idCtrl, DWORD dwBtns, LPCTSTR pszHomeURL, bool fBtnText)
{
    CRect rc;
    pDlg->GetDlgItem(idCtrl)->GetWindowRect(&rc);
    pDlg->ScreenToClient(&rc);
    pDlg->GetDlgItem(idCtrl)->DestroyWindow();

    return Create(pDlg, rc, dwBtns, pszHomeURL, fBtnText);
}

CString CHHCtrl::GetType() const
{
    ASSERT(m_pBrowserApp != NULL);

    BSTR bstr;
    m_pBrowserApp->get_Type(&bstr);
    CString retVal(bstr);
    return retVal;
}

long CHHCtrl::GetLeft() const
{
    ASSERT(m_pBrowserApp != NULL);

    long result;
    m_pBrowserApp->get_Left(&result);
    return result;
}


long CHHCtrl::GetTop() const
{
    ASSERT(m_pBrowserApp != NULL);
    long result;
    m_pBrowserApp->get_Top(&result);
    return result;
}

long CHHCtrl::GetHeight() const
{
    ASSERT(m_pBrowserApp != NULL);
    long result;
    m_pBrowserApp->get_Height(&result);
    return result;
}

BOOL CHHCtrl::GetVisible() const
{
    ASSERT(m_pBrowserApp != NULL);

    VARIANT_BOOL result;
    m_pBrowserApp->get_Visible(&result);
    return result;
}

CString CHHCtrl::GetLocationName() const
{
    ASSERT(m_pBrowserApp != NULL);

    BSTR bstr;
    m_pBrowserApp->get_LocationName(&bstr);
    CString retVal(bstr);
    return retVal;
}

CString CHHCtrl::GetLocationURL() const
{
    ASSERT(m_pBrowserApp != NULL);

    BSTR bstr;
    m_pBrowserApp->get_LocationURL(&bstr);
    CString retVal(bstr);
    return retVal;
}

BOOL CHHCtrl::GetBusy() const
{
    ASSERT(m_pBrowserApp != NULL);

    VARIANT_BOOL result;
    m_pBrowserApp->get_Busy(&result);
    return result;
}

READYSTATE CHHCtrl::GetReadyState() const
{
    ASSERT(m_pBrowserApp != NULL);

    READYSTATE result;
    m_pBrowserApp->get_ReadyState(&result);
    return result;
}

BOOL CHHCtrl::GetOffline() const
{
    ASSERT(m_pBrowserApp != NULL);

    VARIANT_BOOL result;
    m_pBrowserApp->get_Offline(&result);
    return result;
}

BOOL CHHCtrl::GetSilent() const
{
    ASSERT(m_pBrowserApp != NULL);

    VARIANT_BOOL result;
    m_pBrowserApp->get_Silent(&result);
    return result;
}

LPDISPATCH CHHCtrl::GetApplication() const
{
    ASSERT(m_pBrowserApp != NULL);

    LPDISPATCH result;
    m_pBrowserApp->get_Application(&result);
    return result;
}


LPDISPATCH CHHCtrl::GetParentBrowser() const
{
    ASSERT(m_pBrowserApp != NULL);

    LPDISPATCH result;
    m_pBrowserApp->get_Parent(&result);
    return result;
}

LPDISPATCH CHHCtrl::GetContainer() const
{
    ASSERT(m_pBrowserApp != NULL);

    LPDISPATCH result;
    m_pBrowserApp->get_Container(&result);
    return result;
}

LPDISPATCH CHHCtrl::GetHtmlDocument() const
{
    ASSERT(m_pBrowserApp != NULL);

    LPDISPATCH result;
    m_pBrowserApp->get_Document(&result);
    return result;
}

BOOL CHHCtrl::GetTopLevelContainer() const
{
    ASSERT(m_pBrowserApp != NULL);

    VARIANT_BOOL result;
    m_pBrowserApp->get_TopLevelContainer(&result);
    return result;
}

OLECMDF CHHCtrl::QueryStatusWB(OLECMDID cmdID) const
{
    ASSERT(m_pBrowserApp != NULL);

    OLECMDF result;
    m_pBrowserApp->QueryStatusWB(cmdID, &result);
    return result;
}

void CHHCtrl::ExecWB(OLECMDID cmdID, OLECMDEXECOPT cmdexecopt,
    VARIANT* pvaIn, VARIANT* pvaOut)
{
    ASSERT(m_pBrowserApp != NULL);

    m_pBrowserApp->ExecWB(cmdID, cmdexecopt, pvaIn, pvaOut);
}

BOOL CHHCtrl::GetRegisterAsDropTarget() const
{
    ASSERT(m_pBrowserApp != NULL);

    VARIANT_BOOL result;
    m_pBrowserApp->get_RegisterAsDropTarget(&result);
    return result;
}

HRESULT CHHCtrl::Navigate(LPCTSTR lpszURL, DWORD dwFlags /* = 0 */,
    LPCTSTR lpszTargetFrameName /* = NULL */ ,
    LPCTSTR lpszHeaders /* = NULL */, LPVOID lpvPostData /* = NULL */,
    DWORD dwPostDataLen /* = 0 */)
{
    CString strURL(lpszURL);
    BSTR bstrURL = strURL.AllocSysString();

    COleSafeArray vPostData;
    if (lpvPostData != NULL)
    {
        if (dwPostDataLen == 0)
            dwPostDataLen = lstrlen((LPCTSTR) lpvPostData);

        vPostData.CreateOneDim(VT_UI1, dwPostDataLen, lpvPostData);
    }

    return m_pBrowserApp->Navigate(bstrURL,
        COleVariant((long) dwFlags, VT_I4),
        COleVariant(lpszTargetFrameName, VT_BSTR),
        vPostData,
        COleVariant(lpszHeaders, VT_BSTR));
}

void CHHCtrl::Navigate2(LPITEMIDLIST pIDL, DWORD dwFlags /* = 0 */,
    LPCTSTR lpszTargetFrameName /* = NULL */)
{
    ASSERT(m_pBrowserApp != NULL);

    COleVariant vPIDL(pIDL);
    COleVariant empty;

    m_pBrowserApp->Navigate2(vPIDL,
        COleVariant((long) dwFlags, VT_I4),
        COleVariant(lpszTargetFrameName, VT_BSTR),
        empty, empty);
}

void CHHCtrl::Navigate2(LPCTSTR lpszURL, DWORD dwFlags /* = 0 */,
    LPCTSTR lpszTargetFrameName /* = NULL */,
    LPCTSTR lpszHeaders /* = NULL */,
    LPVOID lpvPostData /* = NULL */, DWORD dwPostDataLen /* = 0 */)
{
    ASSERT(m_pBrowserApp != NULL);

    COleSafeArray vPostData;
    if (lpvPostData != NULL)
    {
        if (dwPostDataLen == 0)
            dwPostDataLen = lstrlen((LPCTSTR) lpvPostData);

        vPostData.CreateOneDim(VT_UI1, dwPostDataLen, lpvPostData);
    }

    COleVariant vURL(lpszURL, VT_BSTR);
    COleVariant vHeaders(lpszHeaders, VT_BSTR);
    COleVariant vTargetFrameName(lpszTargetFrameName, VT_BSTR);
    COleVariant vFlags((long) dwFlags, VT_I4);

    m_pBrowserApp->Navigate2(vURL,
        vFlags, vTargetFrameName, vPostData, vHeaders);
}

void CHHCtrl::Navigate2(LPCTSTR lpszURL, DWORD dwFlags,
    CByteArray& baPostData, LPCTSTR lpszTargetFrameName /* = NULL */,
    LPCTSTR lpszHeaders /* = NULL */)
{
    ASSERT(m_pBrowserApp != NULL);

    COleVariant vPostData = baPostData;
    COleVariant vURL(lpszURL, VT_BSTR);
    COleVariant vHeaders(lpszHeaders, VT_BSTR);
    COleVariant vTargetFrameName(lpszTargetFrameName, VT_BSTR);
    COleVariant vFlags((long) dwFlags, VT_I4);

    ASSERT(m_pBrowserApp != NULL);

    m_pBrowserApp->Navigate2(vURL, vFlags, vTargetFrameName,
        vPostData, vHeaders);
}

void CHHCtrl::PutProperty(LPCTSTR lpszProperty, const VARIANT& vtValue)
{
    ASSERT(m_pBrowserApp != NULL);

    CString strProp(lpszProperty);
    BSTR bstrProp = strProp.AllocSysString();
    m_pBrowserApp->PutProperty(bstrProp, vtValue);
    ::SysFreeString(bstrProp);
}

BOOL CHHCtrl::GetProperty(LPCTSTR lpszProperty, CString& strValue)
{
    ASSERT(m_pBrowserApp != NULL);

    CString strProperty(lpszProperty);
    BSTR bstrProperty = strProperty.AllocSysString();

    BOOL bResult = FALSE;
    VARIANT vReturn;
    vReturn.vt = VT_BSTR;
    vReturn.bstrVal = NULL;
    HRESULT hr = m_pBrowserApp->GetProperty(bstrProperty, &vReturn);

    if (SUCCEEDED(hr)) {
        strValue = CString(vReturn.bstrVal);
        bResult = TRUE;
    }

    ::SysFreeString(bstrProperty);
    return bResult;
}

COleVariant CHHCtrl::GetProperty(LPCTSTR lpszProperty)
{
    COleVariant result;

    static BYTE parms[] =
        VTS_BSTR;
    InvokeHelper(0x12f, DISPATCH_METHOD,
        VT_VARIANT, (void*)&result, parms, lpszProperty);

    return result;
}

CString CHHCtrl::GetFullName() const
{
    ASSERT(m_pBrowserApp != NULL);

    BSTR bstr;
    m_pBrowserApp->get_FullName(&bstr);
    CString retVal(bstr);
    return retVal;
}

/////////////////////////////////////////////////////////////////////////////
// CHHCtrl event reflectors

void CHHCtrl::NavigateComplete2(LPDISPATCH /* pDisp */, VARIANT* URL)
{
    ASSERT(V_VT(URL) == VT_BSTR);

    // USES_CONVERSION;

    CString str(V_BSTR(URL));
    OnNavigateComplete2(str);
}

void CHHCtrl::BeforeNavigate2(LPDISPATCH /* pDisp */, VARIANT* URL,
        VARIANT* Flags, VARIANT* TargetFrameName,
        VARIANT* PostData, VARIANT* Headers, BOOL* pfCancel)
{
    ASSERT(V_VT(URL) == VT_BSTR);
    ASSERT(V_VT(TargetFrameName) == VT_BSTR);
    ASSERT(V_VT(PostData) == (VT_VARIANT | VT_BYREF));
    ASSERT(V_VT(Headers) == VT_BSTR);
    ASSERT(pfCancel != NULL);

    // USES_CONVERSION;

    VARIANT* vtPostedData = V_VARIANTREF(PostData);
    CByteArray array;
    if (V_VT(vtPostedData) & VT_ARRAY) {
        // must be a vector of bytes
        ASSERT(vtPostedData->parray->cDims == 1 && vtPostedData->parray->cbElements == 1);

        vtPostedData->vt |= VT_UI1;
        COleSafeArray safe(vtPostedData);

        DWORD dwSize = safe.GetOneDimSize();
        LPVOID pVoid;
        safe.AccessData(&pVoid);

        array.SetSize(dwSize);
        LPBYTE lpByte = array.GetData();

        memcpy(lpByte, pVoid, dwSize);
        safe.UnaccessData();
    }
    // make real parameters out of the notification

    CString strTargetFrameName(V_BSTR(TargetFrameName));
    CString strURL = V_BSTR(URL);
    CString strHeaders = V_BSTR(Headers);
    DWORD nFlags = V_I4(Flags);

    // notify the user's class
    OnBeforeNavigate2(strURL, nFlags, strTargetFrameName, array, strHeaders, pfCancel);
}

void CHHCtrl::DocumentComplete(LPDISPATCH pDisp, VARIANT* URL)
{
    UNUSED_ALWAYS(pDisp);
    ASSERT(V_VT(URL) == VT_BSTR);

    CString str(V_BSTR(URL));
    OnDocumentComplete(str);
}

/////////////////////////////////////////////////////////////////////////////
// CHHCtrl Events

void CHHCtrl::OnProgressChange(long lProgress, long lProgressMax)
{
    UNUSED_ALWAYS(lProgress);
    UNUSED_ALWAYS(lProgressMax);
}

void CHHCtrl::OnCommandStateChange(long lCommand, BOOL bEnable)
{
    switch (lCommand) {
        case CSC_NAVIGATEFORWARD:
            if (m_pTB)
                m_pTB->EnableButton(HH_TB_FORWARD, bEnable);
            break;

        case CSC_NAVIGATEBACK:
            if (m_pTB)
                m_pTB->EnableButton(HH_TB_BACK, bEnable);
            break;
    }
}

void CHHCtrl::OnDownloadBegin()
{
}

void CHHCtrl::OnQuit()
{
}

void CHHCtrl::OnDocumentComplete(LPCTSTR lpszURL)
{
    UNUSED_ALWAYS(lpszURL);
}

void CHHCtrl::OnDownloadComplete()
{
}

void CHHCtrl::OnTitleChange(LPCTSTR lpszText)
{
    UNUSED_ALWAYS(lpszText);
}

void CHHCtrl::OnPropertyChange(LPCTSTR lpszProperty)
{
    UNUSED_ALWAYS(lpszProperty);
}

void CHHCtrl::OnNewWindow2(LPDISPATCH* ppDisp, BOOL* pfCancel)
{
    *pfCancel = FALSE;  // default to continuing

    UNUSED_ALWAYS(ppDisp);
}

void CHHCtrl::OnVisible(BOOL fVisible)
{
    UNUSED_ALWAYS(fVisible);
}

void CHHCtrl::OnBeforeNavigate2(LPCTSTR lpszURL, DWORD nFlags,
    LPCTSTR lpszTargetFrameName, CByteArray& baPostData,
    LPCTSTR lpszHeaders, BOOL* pfCancel)
{
    *pfCancel = FALSE;    // default to continuing

    UNUSED_ALWAYS(lpszURL);
    UNUSED_ALWAYS(nFlags);
    UNUSED_ALWAYS(lpszTargetFrameName);
    UNUSED_ALWAYS(baPostData);
    UNUSED_ALWAYS(lpszHeaders);
}

void CHHCtrl::OnStatusTextChange(LPCTSTR pszText)
{
    UNUSED_ALWAYS(pszText);
}

void CHHCtrl::OnNavigateComplete2(LPCTSTR strURL)
{
    UNUSED_ALWAYS(strURL);
}


HRESULT CHHCtrl::NavigateChm(LPCTSTR URL, LPCTSTR lpszTargetFrameName)
{
    ASSERT(!m_cszChm.IsEmpty());    // You must call SetChmFile() first
    if (m_cszChm.IsEmpty())
        return STG_E_FILENOTFOUND;  // CHM file wasn't located

    char szURL[MAX_PATH * 2];
    wsprintf(szURL, "its:%s::%s", (LPCTSTR) m_cszChm, URL);
    return Navigate(szURL, NULL, lpszTargetFrameName);
}

bool CHHCtrl::SetChmFile(LPCTSTR pszChm)
{
    TCHAR szCHM[MAX_PATH];
    if (!FindChmFile(pszChm, szCHM)) {
        CString csz;
        AfxFormatString1(csz, IDS_HHCTRL_NO_CHM, pszChm);
        AfxMessageBox(csz, MB_ICONEXCLAMATION);
		exit(0);
        return false;
    }
    m_cszChm = szCHM;
    return true;
}

// Look for the CHM file -- this is basically the same algorithm that
// HTML Help uses with the addition of looking in the same location as
// the executable.

bool CHHCtrl::FindChmFile(LPCTSTR pszFile, LPTSTR pszDst)
{
    if (GetFileAttributes(pszFile) != HFILE_ERROR) {
        GetFullPathName(pszFile, MAX_PATH, pszDst, NULL);
        return true;
    }

    // turn all forward slashes into back slashes

    TCHAR szCHM[1024];
    strcpy(szCHM, pszFile); // copy so we can modify
    // Convert forward slashes to backslashes for consistency
    LPTSTR pszTmp = StrChr(szCHM, '/');
    while (pszTmp) {
        *pszTmp = '\\';
        pszTmp = StrChr(pszTmp + 1, '/');
    }
    pszTmp = StrRChr(szCHM, '\\');
    if (pszTmp)
        pszTmp++;
    else
        pszTmp = szCHM;

    { // Try the same directory as the executable
        char szModule[MAX_PATH];
        GetModuleFileName(NULL, szModule, sizeof(szModule));
        LPTSTR pszFile = StrRChr(szModule, '\\');
        if (pszFile) {
            strcpy(pszFile + 1, pszTmp);
            if (GetFileAttributes(szModule) != HFILE_ERROR) {
                strcpy(pszDst, szModule);
                return true;
            }
        }
    }

    // Try the current directory

    if (GetFileAttributes(pszTmp) != HFILE_ERROR) {
        GetFullPathName(pszTmp, MAX_PATH, pszDst, NULL);
        return true;
    }

    // Try the registry

    HKEY hkey;
    LONG result;

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,  "Software\\Microsoft\\Windows\\HTML Help",
            0, KEY_READ, &hkey) == ERROR_SUCCESS) {
        DWORD cbPath = MAX_PATH;
        DWORD type;
        result = RegQueryValueEx(hkey, pszTmp, 0, &type, (PBYTE) pszDst, &cbPath);
        RegCloseKey(hkey);

        if (result == ERROR_SUCCESS) {
            AddTrailingBackslash(pszDst);
            strcat(pszDst, pszTmp);
            if (GetFileAttributes(pszDst) != HFILE_ERROR) {
                return true;
            }
        }
    }

    // Try the Windows\help directory

    GetRegWindowsDirectory(pszDst);
    AddTrailingBackslash(pszDst);
    strcat(pszDst, "Help");
    AddTrailingBackslash(pszDst);
    strcat(pszDst, pszTmp);
    if (GetFileAttributes(pszDst) != HFILE_ERROR) 
        return true;

    // Try hh.ini

    if (GetPrivateProfileString("files", pszTmp, "", pszDst, MAX_PATH, "hh.ini") > 1) {
        TCHAR* pszComma = StrChr(pszDst, ',');
        if (pszComma)
            *pszComma++ = 0;
        if (GetFileAttributes(pszDst) != HFILE_ERROR) {
            return true;
        }
        if (pszComma && *pszComma) {
            do {
                if (AfxMessageBox(pszComma, MB_OKCANCEL | MB_ICONHAND) != IDOK)
                    break;
            } while (GetFileAttributes(pszDst) == HFILE_ERROR);
            return true;
        }
    }

    return false;
}

// This function supports diskless workstations -- GetWindowsDirectory() won't
// always give the same result in that environment

void CHHCtrl::GetRegWindowsDirectory(TCHAR* pszDst)
{
    HKEY hkey;

    DWORD cbPath = MAX_PATH;
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
           "Software\\Microsoft\\Windows\\CurrentVersion\\Setup", 0, KEY_READ, &hkey) ==
            ERROR_SUCCESS) {
        DWORD type;
        RegQueryValueEx(hkey, "SharedDir", 0, &type, (PBYTE) pszDst, &cbPath);
        RegCloseKey(hkey);
    }

    if (cbPath == MAX_PATH) // means couldn't read registry key
        GetWindowsDirectory(pszDst, MAX_PATH);
}

// DBCS-enabled searching

LPTSTR CHHCtrl::StrChr(LPCTSTR psz, TCHAR ch)
{
    while (*psz && *psz != ch) 
        psz = CharNext(psz);
    return (*psz ? (TCHAR*) psz : NULL);
}

LPTSTR CHHCtrl::StrRChr(LPCTSTR psz, TCHAR ch)
{
    LPCTSTR pszLastFound = StrChr(psz, ch);
    if (!pszLastFound)
        return NULL;
    while ((psz = StrChr(pszLastFound + 1, ch)))
        pszLastFound = psz;
    return (LPTSTR) pszLastFound;
}

void CHHCtrl::AddTrailingBackslash(LPTSTR psz)
{
    ASSERT(psz && *psz);
    TCHAR* pszEnd = psz + strlen(psz);
    if (*(CharPrev(psz, pszEnd)) != '\\' &&
            *(CharPrev(psz, pszEnd)) != '/' &&
            *(CharPrev(psz, pszEnd)) != ':') {
        *pszEnd++ = '\\';
        *pszEnd++ = '\0';
    }
}

HRESULT CHHCtrl::NavigateChm(UINT mapID, LPCTSTR lpszTargetFrameName)
{
    ASSERT(!m_cszChm.IsEmpty());    // You must call SetChmFile() first
    if (m_cszChm.IsEmpty())
        return STG_E_PATHNOTFOUND ;  // CHM file wasn't located

    if (!m_paMapIds) {
        CItsFile its;   // ITS and CHM files are effectively the same thing
        if (FAILED(its.OpenITS(m_cszChm))) // theoretically impossible as SetChmFile() should have failed first
            return STG_E_PATHNOTFOUND ;  // CHM file wasn't located

        IStream* pStream;
        if (FAILED(its.OpenStream("#IVB", &pStream, its.GetStorage()))) {
            TRACE1("Missing [MAP] section in the file %s", m_cszChm);
            return STG_E_FILENOTFOUND;
        }

        ULONG cbRead;
        DWORD cbMapIds;

        if (FAILED(pStream->Read(&cbMapIds, sizeof(DWORD), &cbRead)) || (cbRead != sizeof(DWORD))) {
            pStream->Release();
            return STG_E_READFAULT;
        }
        if (!(m_paMapIds = (MAPID*) LocalAlloc(LMEM_FIXED, cbMapIds))) {
            pStream->Release();
            return E_OUTOFMEMORY ;
        }
        if (FAILED(pStream->Read(m_paMapIds, cbMapIds, &cbRead)) || (cbRead != (ULONG) cbMapIds)) {
            pStream->Release();
            return STG_E_READFAULT;
        }
        pStream->Release();

        m_cMapIds = cbMapIds / sizeof(MAPID);
    }
    for (int i = 0; i < m_cMapIds; i++) {
        if (mapID == m_paMapIds[i].mapid) {
            CItsFile its;   // ITS and CHM files are effectively the same thing
            if (FAILED(its.OpenITS(m_cszChm))) // theoretically impossible as SetChmFile() should have failed first
                return STG_E_PATHNOTFOUND ;  // CHM file wasn't located
            CStringSubFile ssf(&its);
            TCHAR szURL[MAX_PATH * 2];
            wsprintf(szURL, "its:%s::", (LPCTSTR) m_cszChm);
            ssf.GetString(m_paMapIds[i].offset, szURL + strlen(szURL), sizeof(szURL) - strlen(szURL));
            return Navigate(szURL, NULL, lpszTargetFrameName);
        }
    }

    return E_FAIL;  // couldn't find the requested map id
}

void CHHCtrl::OnCommand(DWORD id)
{
    switch (id) {
        case HH_TB_HOME:
            ASSERT(m_pBrowserApp);

            // Check for a ':' in the URL in case the URL has a protocol (file:, http:, etc.)

            if (!m_cszChm.IsEmpty() && StrChr(m_cszHome, ':') == NULL) {
                if (!m_cszHome.IsEmpty())
                    NavigateChm(m_cszHome);
                else if (m_mapHome)
                    NavigateChm(m_mapHome);
            }
            else
                Navigate(m_cszHome);
            break;

        case HH_TB_BACK:
            GoBack();
            break;

        case HH_TB_FORWARD:
            GoForward();
            break;

        case HH_TB_PRINT:
            OnFilePrint();
            break;

        case HH_TB_CLOSE:
            GetParent()->PostMessage(WM_CLOSE, 0, 0);
            break;
    }
}

void CHHCtrl::MoveWindow(CRect* prc, BOOL fRepaint)
{
    if (m_pTB) {
        CRect rcBtn;
        m_pTB->GetClientRect(&rcBtn);
        rcBtn.OffsetRect(prc->left, prc->top);
        ::MoveWindow(m_pTB->m_hWnd, rcBtn.left, rcBtn.top, rcBtn.Width(), rcBtn.Height(), fRepaint);
        prc->top += rcBtn.Height() + TB_PAD;
    }
    ::MoveWindow(m_hWnd, prc->left, prc->top, prc->Width(), prc->Height(), fRepaint);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
CEO Bttlxe Ltd & Incentica Ltd
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions