|
|
#include "priv.h"
#include "dspsprt.h"
#include <hlink.h>
#include "iface.h"
#include "resource.h"
#include <mluisupp.h>
#include "shdocfl.h"
class CTravelLog;
class CEnumEntry : public IEnumTravelLogEntry { public: // *** IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
// *** IEnumTravelLogEntry specific methods
STDMETHODIMP Next(ULONG cElt, ITravelLogEntry **rgElt, ULONG *pcEltFetched); STDMETHODIMP Skip(ULONG cElt); STDMETHODIMP Reset(); STDMETHODIMP Clone(IEnumTravelLogEntry **ppEnum);
CEnumEntry(); void Init(CTravelLog *ptl, IUnknown *punk, DWORD dwOffset, DWORD dwFlags); void SetBase();
protected: ~CEnumEntry(); LONG _cRef; DWORD _dwFlags; DWORD _dwOffset; LONG _lStart; CTravelLog *_ptl; IUnknown *_punk; };
class CTravelEntry : public ITravelEntry, public ITravelLogEntry, public IPropertyBag { public: CTravelEntry(BOOL fIsLocalAnchor);
// *** IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
// *** ITravelEntry specific methods
STDMETHODIMP Update(IUnknown *punk, BOOL fIsLocalAnchor); STDMETHODIMP Invoke(IUnknown *punk); STDMETHODIMP GetPidl(LPITEMIDLIST *ppidl); // *** ITravelLogEntry specific methods
STDMETHODIMP GetTitle(LPOLESTR *ppszTitle); STDMETHODIMP GetURL(LPOLESTR *ppszURL); // *** IPropertyBag specific methods
STDMETHODIMP Read(LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog); STDMETHODIMP Write(LPCOLESTR pszPropName, VARIANT *pVar);
static HRESULT CreateTravelEntry(IBrowserService *pbs, BOOL fIsLocalAnchor, CTravelEntry **ppte); void SetPrev(CTravelEntry *ptePrev); void SetNext(CTravelEntry *pteNext); CTravelEntry *GetPrev() {return _ptePrev;} CTravelEntry *GetNext() {return _pteNext;} void RemoveSelf(); BOOL CanInvoke(IUnknown *punk, BOOL fAllowLocalAnchor); HRESULT GetIndexBrowser(IUnknown *punkIn, IUnknown ** ppsbOut) const; DWORD Size(); DWORD ListSize(); HRESULT Clone(CTravelEntry **ppte); HRESULT UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext); HRESULT UpdateSelf(IUnknown *punk) {return Update(punk, (_type == TET_LOCALANCHOR));} BOOL IsExternal(void) { return (_type==TET_EXTERNALNAV); } HRESULT GetDisplayName(LPTSTR psz, DWORD cch, DWORD dwFlags); BOOL IsEqual(LPCITEMIDLIST pidl) {return ILIsEqual(pidl, _pidl);} BOOL IsLocalAnchor(void) { return (_type==TET_LOCALANCHOR);} protected: CTravelEntry(void); HRESULT _InvokeExternal(IUnknown *punk); HRESULT _UpdateTravelLog(IUnknown *punk, BOOL fIsLocalAnchor); HRESULT _UpdateFromTLClient(IUnknown * punk, IStream ** ppStream); LONG _cRef;
~CTravelEntry(); void _Reset(void); enum { TET_EMPTY = 0, TET_DEFAULT = 1, TET_LOCALANCHOR, TET_EXTERNALNAV };
DWORD _type; // flags for our own sake...
LPITEMIDLIST _pidl; // pidl of the entry
HGLOBAL _hGlobalData; // the stream data saved by the entry
DWORD _bid; // the BrowserIndex for frame specific navigation
DWORD _dwCookie; // if _hGlobalData is NULL the cookie should be set
WCHAR * _pwzTitle; WCHAR * _pwzUrlLocation; IHlink *_phl; IHlinkBrowseContext *_phlbc; IPropertyBag *_ppb;
CTravelEntry *_ptePrev; CTravelEntry *_pteNext; };
CTravelEntry::CTravelEntry(BOOL fIsLocalAnchor) : _cRef(1) { //these should always be allocated
// thus they will always start 0
if (fIsLocalAnchor) _type = TET_LOCALANCHOR; else ASSERT(!_type);
ASSERT(!_pwzTitle); ASSERT(!_pwzUrlLocation); ASSERT(!_pidl); ASSERT(!_hGlobalData); ASSERT(!_bid); ASSERT(!_dwCookie); ASSERT(!_ptePrev); ASSERT(!_pteNext); ASSERT(!_phl); ASSERT(!_ppb); ASSERT(!_phlbc); TraceMsg(TF_TRAVELLOG, "TE[%X] created _type = %x", this, _type); }
CTravelEntry::CTravelEntry(void) : _cRef(1) { ASSERT(!_type); ASSERT(!_pwzTitle); ASSERT(!_pwzUrlLocation); ASSERT(!_pidl); ASSERT(!_hGlobalData); ASSERT(!_bid); ASSERT(!_dwCookie); ASSERT(!_ptePrev); ASSERT(!_pteNext); ASSERT(!_phl); ASSERT(!_ppb); ASSERT(!_phlbc);
TraceMsg(TF_TRAVELLOG, "TE[%X] created", this, _type); }
HGLOBAL CloneHGlobal(HGLOBAL hGlobalIn) { DWORD dwSize = (DWORD)GlobalSize(hGlobalIn); HGLOBAL hGlobalOut = GlobalAlloc(GlobalFlags(hGlobalIn), dwSize); HGLOBAL hGlobalResult = NULL;
if (NULL != hGlobalOut) { LPVOID pIn= GlobalLock(hGlobalIn);
if (NULL != pIn) { LPVOID pOut= GlobalLock(hGlobalOut);
if (NULL != pOut) { memcpy(pOut, pIn, dwSize); GlobalUnlock(hGlobalOut); hGlobalResult = hGlobalOut; }
GlobalUnlock(hGlobalIn); }
if (!hGlobalResult) { GlobalFree(hGlobalOut); hGlobalOut = NULL; } }
return hGlobalResult; }
HRESULT CTravelEntry::Clone(CTravelEntry **ppte) { // dont ever clone an external entry
if (_type == TET_EXTERNALNAV) return E_FAIL;
HRESULT hr = S_OK; CTravelEntry *pte = new CTravelEntry(); if (pte) { pte->_type = _type; pte->_bid = _bid; pte->_dwCookie = _dwCookie;
if (_pwzTitle) { pte->_pwzTitle = StrDup(_pwzTitle); if (!pte->_pwzTitle) { hr = E_OUTOFMEMORY; } }
if (_pwzUrlLocation) { pte->_pwzUrlLocation = StrDup(_pwzUrlLocation); if (!pte->_pwzUrlLocation) { hr = E_OUTOFMEMORY; } }
if (_pidl) { pte->_pidl = ILClone(_pidl); if (!pte->_pidl) hr = E_OUTOFMEMORY; } else pte->_pidl = NULL;
if (_hGlobalData) { pte->_hGlobalData = CloneHGlobal(_hGlobalData); if (NULL == pte->_hGlobalData) { hr = E_OUTOFMEMORY; } } else { ASSERT(NULL == pte->_hGlobalData); } } else hr = E_OUTOFMEMORY;
if (FAILED(hr) && pte) { pte->Release(); *ppte = NULL; } else *ppte = pte;
TraceMsg(TF_TRAVELLOG, "TE[%X] Clone hr = %x", this, hr);
return hr; }
CTravelEntry::~CTravelEntry() { ILFree(_pidl);
if (_hGlobalData) { GlobalFree(_hGlobalData); _hGlobalData = NULL; }
if (_pwzTitle) { LocalFree(_pwzTitle); _pwzTitle = NULL; }
if (_pwzUrlLocation) { LocalFree(_pwzUrlLocation); _pwzUrlLocation = NULL; }
if (_pteNext) { _pteNext->Release(); }
// Don't need to release _ptePrev because TravelEntry only addref's pteNext
ATOMICRELEASE(_ppb); ATOMICRELEASE(_phl); ATOMICRELEASE(_phlbc); TraceMsg(TF_TRAVELLOG, "TE[%X] destroyed ", this); }
HRESULT CTravelEntry::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CTravelEntry, ITravelEntry), // IID_ITravelEntry
QITABENT(CTravelEntry, ITravelLogEntry), QITABENT(CTravelEntry, IPropertyBag), { 0 }, }; return QISearch(this, qit, riid, ppvObj); }
ULONG CTravelEntry::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CTravelEntry::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if (0 == cRef) { delete this; } return cRef; }
//+-------------------------------------------------------------------------
//
// Method : CTravelEntry::GetIndexBrowser
//
// Synopsis : This method finds and returns the IUnknown of the browser
// with the index in _bid. This method first checks to see
// if the passed in punk supports ITravelLogClient. If it
// doesn't, it checks for IBrowserService.
//
//--------------------------------------------------------------------------
HRESULT CTravelEntry::GetIndexBrowser(IUnknown * punk, IUnknown ** ppunkBrowser) const { HRESULT hr = E_FAIL; ASSERT(ppunkBrowser);
ITravelLogClient * ptlcTop; hr = punk->QueryInterface(IID_PPV_ARG(ITravelLogClient, &ptlcTop)); if (SUCCEEDED(hr)) { hr = ptlcTop->FindWindowByIndex(_bid, ppunkBrowser); ptlcTop->Release(); }
TraceMsg(TF_TRAVELLOG, "TE[%X]::GetIndexBrowser _bid = %X, hr = %X", this, _bid, hr); return hr; }
//+-------------------------------------------------------------------------
//
// Method : CTravelEntry::CanInvoke
//
// Synopsis : This method determines if the current travel entry can
// be invoked. There are two criteria that determine if
// this entry can be invoked.
// 1) If the entry is a local anchor, fAllowLocalAnchor must
// be TRUE.
// 2) A browser with the index in _bid must exist.
//
//--------------------------------------------------------------------------
BOOL CTravelEntry::CanInvoke(IUnknown *punk, BOOL fAllowLocalAnchor) { IUnknown * punkBrowser = NULL; BOOL fRet = IsLocalAnchor() ? fAllowLocalAnchor : TRUE;
fRet = fRet && SUCCEEDED(GetIndexBrowser(punk, &punkBrowser));
SAFERELEASE(punkBrowser);
return fRet; }
DWORD CTravelEntry::Size() { DWORD cbSize = SIZEOF(*this);
if (_pidl) cbSize += ILGetSize(_pidl);
if (_hGlobalData) { cbSize += (DWORD)GlobalSize(_hGlobalData); }
if (_pwzTitle) { cbSize += (DWORD)LocalSize(_pwzTitle); }
if (_pwzUrlLocation) { cbSize += (DWORD)LocalSize(_pwzUrlLocation); }
return cbSize; }
DWORD CTravelEntry::ListSize() { CTravelEntry *pte = GetNext();
DWORD cb = Size(); while (pte) { cb += pte->Size(); pte = pte->GetNext(); } return cb; }
void CTravelEntry::_Reset() { Pidl_Set(&_pidl, NULL);
if (NULL != _hGlobalData) { GlobalFree(_hGlobalData); _hGlobalData = NULL; }
ATOMICRELEASE(_phl); ATOMICRELEASE(_phlbc);
_bid = 0; _type = TET_EMPTY; _dwCookie = 0;
if (_pwzTitle) { LocalFree(_pwzTitle); _pwzTitle = NULL; }
if (_pwzUrlLocation) { LocalFree(_pwzUrlLocation); _pwzUrlLocation = NULL; }
TraceMsg(TF_TRAVELLOG, "TE[%X]::_Reset", this); }
HRESULT CTravelEntry::_UpdateTravelLog(IUnknown *punk, BOOL fIsLocalAnchor) { IBrowserService *pbs; HRESULT hr = E_FAIL; // we need to update here
if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { ITravelLog *ptl; if (SUCCEEDED(pbs->GetTravelLog(&ptl))) { hr = ptl->UpdateEntry(punk, fIsLocalAnchor); ptl->Release(); } pbs->Release(); }
return hr; }
HRESULT CTravelEntry::_InvokeExternal(IUnknown *punk) { HRESULT hr = E_FAIL;
ASSERT(_phl); ASSERT(_phlbc); TraceMsg(TF_TRAVELLOG, "TE[%X]::InvokeExternal entered on _bid = %X, _phl = %X, _phlbc = %X", this, _bid, _phl, _phlbc);
HWND hwnd = NULL; IOleWindow *pow; if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IOleWindow, &pow)))) { pow->GetWindow(&hwnd); pow->Release(); }
// set the size and position of the browser frame window, so that the
// external target can sync up its frame window to those coordinates
HLBWINFO hlbwi = {0};
hlbwi.cbSize = sizeof(hlbwi); hlbwi.grfHLBWIF = 0;
if (hwnd) { WINDOWPLACEMENT wp = {0};
wp.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(hwnd, &wp); hlbwi.grfHLBWIF = HLBWIF_HASFRAMEWNDINFO; hlbwi.rcFramePos = wp.rcNormalPosition; if (wp.showCmd == SW_SHOWMAXIMIZED) hlbwi.grfHLBWIF |= HLBWIF_FRAMEWNDMAXIMIZED; }
_phlbc->SetBrowseWindowInfo(&hlbwi);
//
// right now we always now we are going back, but later on
// maybe we should ask the browser whether this is back or forward
//
hr = _phl->Navigate(HLNF_NAVIGATINGBACK, NULL, NULL, _phlbc);
IWebBrowser2 *pwb; if (SUCCEEDED(IUnknown_QueryService(punk, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &pwb)))) { ASSERT(pwb); pwb->put_Visible(FALSE); pwb->Release(); }
_UpdateTravelLog(punk, FALSE);
TraceMsg(TF_TRAVELLOG, "TE[%X]::InvokeExternal exited hr = %X", this, hr);
return hr; }
HRESULT CTravelEntry::Invoke(IUnknown *punk) { IPersistHistory *pph = NULL; HRESULT hr = E_FAIL; IUnknown * punkBrowser = NULL; IHTMLWindow2 * pWindow = NULL;
TraceMsg(TF_TRAVELLOG, "TE[%X]::Invoke entered on _bid = %X", this, _bid); TraceMsgW(TF_TRAVELLOG, "TE[%X]::Invoke title '%s'", this, _pwzTitle);
if (_type == TET_EXTERNALNAV) { hr = _InvokeExternal(punk); goto Quit; }
// Get the window/browser with the index. If that
// fails, punk may be a IHTMLWindow2. If so,
// get its IPersistHistory so the travel entry
// can be loaded directly. (This is needed by Trident
// in order to navigate in frames when traveling
// backwards or forwards.
//
hr = GetIndexBrowser(punk, &punkBrowser); if (SUCCEEDED(hr)) { hr = punkBrowser->QueryInterface(IID_PPV_ARG(IPersistHistory, &pph)); } else { hr = punk->QueryInterface(IID_PPV_ARG(IHTMLWindow2, &pWindow)); if (SUCCEEDED(hr)) { hr = pWindow->QueryInterface(IID_PPV_ARG(IPersistHistory, &pph)); } }
if (SUCCEEDED(hr)) { ASSERT(pph);
if (_type == TET_LOCALANCHOR) { ITravelLogClient * pTLClient; hr = pph->QueryInterface(IID_PPV_ARG(ITravelLogClient, &pTLClient)); if (SUCCEEDED(hr)) { hr = pTLClient->LoadHistoryPosition(_pwzUrlLocation, _dwCookie); pTLClient->Release(); } else { hr = pph->SetPositionCookie(_dwCookie); } } else { // we need to clone it
ASSERT(_hGlobalData); HGLOBAL hGlobal = CloneHGlobal(_hGlobalData); if (NULL != hGlobal) { IStream *pstm; hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pstm); if (SUCCEEDED(hr)) { hr = pph->LoadHistory(pstm, NULL); pstm->Release(); } else { GlobalFree(hGlobal); } } else { hr = E_OUTOFMEMORY; } } pph->Release(); }
Quit:
SAFERELEASE(punkBrowser); SAFERELEASE(pWindow);
TraceMsg(TF_TRAVELLOG, "TE[%X]::Invoke exited on _bid = %X, hr = %X", this, _bid, hr); return hr; }
HRESULT CTravelEntry::UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext) { TraceMsg(TF_TRAVELLOG, "TE[%X]::UpdateExternal entered on punk = %X, punkhlbc = %X", this, punk, punkHLBrowseContext);
_Reset(); ASSERT(punkHLBrowseContext); punkHLBrowseContext->QueryInterface(IID_PPV_ARG(IHlinkBrowseContext, &_phlbc)); ASSERT(_phlbc);
_type = TET_EXTERNALNAV;
HRESULT hr = E_FAIL;
//
// right now we only support externals being previous. we never actually navigate
// to another app. we handle everything in pane ourselves.
// so theoretically we never need to worry about HLID_NEXT
_phlbc->GetHlink((ULONG) HLID_PREVIOUS, &_phl); IBrowserService *pbs; punk->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs));
if (pbs && _phl) { _bid = pbs->GetBrowserIndex();
WCHAR *pwszTarget; hr = _phl->GetStringReference(HLINKGETREF_ABSOLUTE, &pwszTarget, NULL); if (SUCCEEDED(hr)) { // create pidl
hr = IECreateFromPath(pwszTarget, &_pidl); OleFree(pwszTarget); } }
ATOMICRELEASE(pbs);
TraceMsg(TF_TRAVELLOG, "TE[%X]::UpdateExternal exited _bid = %X, hr = %X", this, _bid, hr);
return hr; }
HRESULT CTravelEntry::Update(IUnknown *punk, BOOL fIsLocalAnchor) { ASSERT(punk); // this means that we went back to an external app,
// and now we are going forward again. we dont persist
// any state info about them that would be different.
if (_type == TET_EXTERNALNAV) { TraceMsg(TF_TRAVELLOG, "TE[%X]::Update NOOP on external entry", this); return S_OK; }
_Reset(); // Try ITravelLogClient first. If that fails, revert to IBrowserService.
//
IStream *pstm = NULL; IPersistHistory *pph = NULL; HRESULT hr = _UpdateFromTLClient(punk, &pstm); if (S_OK != hr) goto Cleanup;
hr = punk->QueryInterface(IID_PPV_ARG(IPersistHistory, &pph));
ASSERT(SUCCEEDED(hr));
if (S_OK != hr) goto Cleanup;
if (fIsLocalAnchor) { // persist a cookie
//
_type = TET_LOCALANCHOR; hr = pph->GetPositionCookie(&_dwCookie); } else { _type = TET_DEFAULT;
// persist a stream
//
ASSERT(!_hGlobalData);
if (!pstm) { hr = CreateStreamOnHGlobal(NULL, FALSE, &pstm); if (hr != S_OK) goto Cleanup; pph->SaveHistory(pstm); }
STATSTG stg; HRESULT hrStat = pstm->Stat(&stg, STATFLAG_NONAME);
hr = GetHGlobalFromStream(pstm, &_hGlobalData);
// This little exercise here is to shrink the memory block we get from
// the OLE API which allocates blocks in chunks of 8KB. Typical stream
// sizes are only a few hundred bytes.
if (S_OK != hrStat) goto Cleanup; HGLOBAL hGlobalTemp = GlobalReAlloc(_hGlobalData, stg.cbSize.LowPart, GMEM_MOVEABLE); if (NULL != hGlobalTemp) { _hGlobalData = hGlobalTemp; } }
Cleanup: if (FAILED(hr)) _Reset();
SAFERELEASE(pstm); SAFERELEASE(pph);
TraceMsg(TF_TRAVELLOG, "TE[%X]::Update exited on _bid = %X, hr = %X", this, _bid, hr); return hr; }
//+-----------------------------------------------------------------------------
//
// Method : CTravelEntry::_UpdateFromTLClient
//
// Synopsis : Updates the travel entry using the ITravelLogClient interface
//
//------------------------------------------------------------------------------
HRESULT CTravelEntry::_UpdateFromTLClient(IUnknown * punk, IStream ** ppStream) { HRESULT hr; WINDOWDATA windata = {0}; ITravelLogClient * ptlc = NULL; hr = punk->QueryInterface(IID_PPV_ARG(ITravelLogClient, &ptlc)); if (S_OK != hr) goto Cleanup;
hr = ptlc->GetWindowData(&windata); if (S_OK != hr) goto Cleanup; _bid = windata.dwWindowID; ILFree(_pidl);
if (windata.pidl) { _pidl = ILClone(windata.pidl); } else { hr = IEParseDisplayNameWithBCW(windata.uiCP, windata.lpszUrl, NULL, &_pidl); if (S_OK != hr) goto Cleanup; }
ASSERT(_pidl);
// If there is an url location, append it to the end of the url
//
if (_pwzUrlLocation) { LocalFree(_pwzUrlLocation); _pwzUrlLocation = NULL; }
if (windata.lpszUrlLocation && *windata.lpszUrlLocation) { _pwzUrlLocation = StrDup(windata.lpszUrlLocation); }
// Pick up the title as a display name for menus and such.
//
if (_pwzTitle) { LocalFree(_pwzTitle); _pwzTitle = NULL; }
if (windata.lpszTitle) _pwzTitle = StrDup(windata.lpszTitle);
*ppStream = windata.pStream;
TraceMsgW(TF_TRAVELLOG, "TE[%X]::_UpdateFromTLClient - ptlc:[0x%X] _bid:[%ld] Url:[%ws] Title:[%ws] UrlLocation:[%ws] ppStream:[0x%X]", this, ptlc, _bid, windata.lpszUrl, _pwzTitle, _pwzUrlLocation, *ppStream);
Cleanup: ILFree(windata.pidl);
CoTaskMemFree(windata.lpszUrl); CoTaskMemFree(windata.lpszUrlLocation); CoTaskMemFree(windata.lpszTitle); SAFERELEASE(ptlc);
// Don't release windata.pStream. It will
// be released when ppStream is released.
return hr; }
HRESULT CTravelEntry::GetPidl(LPITEMIDLIST * ppidl) { HRESULT hr = E_FAIL; if (EVAL(ppidl)) { hr = SHILClone(_pidl, ppidl); } return hr; }
void CTravelEntry::SetNext(CTravelEntry *pteNext) { if (_pteNext) _pteNext->Release();
_pteNext = pteNext;
if (_pteNext) { _pteNext->_ptePrev = this; } }
void CTravelEntry::SetPrev(CTravelEntry *ptePrev) { _ptePrev = ptePrev; if (_ptePrev) _ptePrev->SetNext(this); }
//
// this is for removing from the middle of the list...
//
void CTravelEntry::RemoveSelf() { if (_pteNext) _pteNext->_ptePrev = _ptePrev;
// remove yourself from the list
if (_ptePrev) { // after this point, we may be destroyed so can't touch any more member vars
_ptePrev->_pteNext = _pteNext; }
_ptePrev = NULL; _pteNext = NULL;
// we lose a reference now because we're gone from _ptePrev's _pteNext
// (or if we were the top of the list, we're also nuked)
Release(); }
HRESULT GetUnescapedUrlIfAppropriate(LPCITEMIDLIST pidl, LPTSTR pszUrl, DWORD cch) { TCHAR szUrl[MAX_URL_STRING];
// The SHGDN_NORMAL display name will be the pretty name (Web Page title) unless
// it's an FTP URL or the web page didn't set a title.
if (SUCCEEDED(IEGetDisplayName(pidl, szUrl, SHGDN_NORMAL)) && UrlIs(szUrl, URLIS_URL)) { // NT #279192, If an URL is escaped, it normally contains three types of
// escaped chars.
// 1) Seperating type chars ('#' for frag, '?' for params, etc.)
// 2) DBCS chars,
// 3) Data (a bitmap in the url by escaping the binary bytes)
// Since #2 is very common, we want to try to unescape it so it has meaning
// to the user. UnEscaping isn't safe if the user can copy or modify the data
// because they could loose data when it's reparsed. One thing we need to
// do for #2 to work is for it to be in ANSI when unescaped. This is needed
// or the DBCS lead and trail bytes will be in unicode as [0x<LeadByte> 0x00]
// [0x<TrailByte> 0x00]. Being in ANSI could cause a problem if the the string normally
// crosses code pages, but that is uncommon or non-existent in the IsURLChild()
// case.
CHAR szUrlAnsi[MAX_URL_STRING];
SHTCharToAnsi(szUrl, szUrlAnsi, ARRAYSIZE(szUrlAnsi)); UrlUnescapeA(szUrlAnsi, NULL, NULL, URL_UNESCAPE_INPLACE|URL_UNESCAPE_HIGH_ANSI_ONLY); SHAnsiToTChar(szUrlAnsi, pszUrl, cch); } else { StrCpyN(pszUrl, szUrl, cch); // Truncate if needed
}
return S_OK; }
#define TEGDN_FORSYSTEM 0x00000001
HRESULT CTravelEntry::GetDisplayName(LPTSTR psz, DWORD cch, DWORD dwFlags) { if (!psz || !cch) return E_INVALIDARG;
psz[0] = 0; if ((NULL != _pwzTitle) && (*_pwzTitle != 0)) { StrCpyNW(psz, _pwzTitle, cch); } else if (_pidl) { GetUnescapedUrlIfAppropriate(_pidl, psz, cch); }
if (dwFlags & TEGDN_FORSYSTEM) { if (!SHIsDisplayable(psz, g_fRunOnFE, g_bRunOnNT5)) { // Display name isn't system-displayable. Just use the path/url instead.
SHTitleFromPidl(_pidl, psz, cch, FALSE); } }
SHCleanupUrlForDisplay(psz); return psz[0] ? S_OK : E_FAIL; }
HRESULT CTravelEntry::GetTitle(LPOLESTR *ppszTitle) { HRESULT hres = S_OK; TCHAR szTitle[MAX_BROWSER_WINDOW_TITLE];
ASSERT(IS_VALID_WRITE_PTR(ppszTitle, LPOLESTR));
hres = GetDisplayName(szTitle, ARRAYSIZE(szTitle), TEGDN_FORSYSTEM); if (SUCCEEDED(hres)) { ASSERT(*szTitle);
hres = SHStrDup(szTitle, ppszTitle); }
return hres; }
HRESULT CTravelEntry::GetURL(LPOLESTR *ppszUrl) { HRESULT hres = E_FAIL; LPITEMIDLIST pidl = NULL; WCHAR wszURL[MAX_URL_STRING];
if (_pidl) hres = ::IEGetDisplayName(_pidl, wszURL, SHGDN_FORPARSING);
if (SUCCEEDED(hres)) hres = SHStrDup(wszURL, ppszUrl);
return hres; }
HRESULT CTravelEntry::Read(LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog) { if (!_ppb) { return E_INVALIDARG; } return _ppb->Read(pszPropName, pVar, pErrorLog); }
HRESULT CTravelEntry::Write(LPCOLESTR pszPropName, VARIANT *pVar) { HRESULT hres = S_OK;
if (!_ppb) { hres = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &_ppb)); } if (SUCCEEDED(hres)) { ASSERT(_ppb); hres = _ppb->Write(pszPropName, pVar); } return hres; }
class CTravelLog : public ITravelLog, public ITravelLogEx { public: // *** IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef() ; STDMETHODIMP_(ULONG) Release();
// *** ITravelLog specific methods
STDMETHODIMP AddEntry(IUnknown *punk, BOOL fIsLocalAnchor); STDMETHODIMP UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor); STDMETHODIMP UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext); STDMETHODIMP Travel(IUnknown *punk, int iOffset); STDMETHODIMP GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte); STDMETHODIMP FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte); STDMETHODIMP GetToolTipText(IUnknown *punk, int iOffset, int idsTemplate, LPWSTR pwzText, DWORD cchText); STDMETHODIMP InsertMenuEntries(IUnknown *punk, HMENU hmenu, int nPos, int idFirst, int idLast, DWORD dwFlags); STDMETHODIMP Clone(ITravelLog **pptl); STDMETHODIMP_(DWORD) CountEntries(IUnknown *punk); STDMETHODIMP Revert(void);
// *** ITravelLogEx specific methods
STDMETHODIMP FindTravelEntryWithUrl(IUnknown * punk, UINT uiCP, LPOLESTR lpszUrl, ITravelEntry ** ppte); STDMETHODIMP TravelToUrl(IUnknown * punk, UINT uiCP, LPOLESTR lpszUrl); STDMETHOD(DeleteIndexEntry)(IUnknown *punk, int index); STDMETHOD(DeleteUrlEntry)(IUnknown *punk, UINT uiCP, LPOLESTR pszUrl); STDMETHOD(CountEntryNodes)(IUnknown *punk, DWORD dwFlags, DWORD *pdwCount); STDMETHOD(CreateEnumEntry)(IUnknown *punk, IEnumTravelLogEntry **ppEnum, DWORD dwFlags); STDMETHOD(DeleteEntry)(IUnknown *punk, ITravelLogEntry *pte); STDMETHOD(InsertEntry)(IUnknown *punkBrowser, ITravelLogEntry *pteRelativeTo, BOOL fPrepend, IUnknown* punkTLClient, ITravelLogEntry **ppEntry); STDMETHOD(TravelToEntry)(IUnknown *punkBrowser, ITravelLogEntry *pteDestination);
CTravelLog();
protected: ~CTravelLog(); HRESULT _FindEntryByOffset(IUnknown *punk, int iOffset, CTravelEntry **ppte); HRESULT _FindEntryByPidl(IUnknown * punk, LPCITEMIDLIST pidl, CTravelEntry ** ppte); HRESULT _FindEntryByPunk(IUnknown * punk, ITravelLogEntry *pteSearch, CTravelEntry ** ppte); void _DeleteFrameSetEntry(IUnknown *punk, CTravelEntry *pte); void _Prune(void); LONG _cRef; DWORD _cbMaxSize; DWORD _cbTotalSize;
CTravelEntry *_pteCurrent; //pteCurrent
CTravelEntry *_pteUpdate; CTravelEntry *_pteRoot; };
CTravelLog::CTravelLog() : _cRef(1) { ASSERT(!_pteCurrent); ASSERT(!_pteUpdate); ASSERT(!_pteRoot);
DWORD dwType, dwSize = SIZEOF(_cbMaxSize), dwDefault = 1024 * 1024; SHRegGetUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\TravelLog"), TEXT("MaxSize"), &dwType, (LPVOID)&_cbMaxSize, &dwSize, FALSE, (void *)&dwDefault, SIZEOF(dwDefault)); TraceMsg(TF_TRAVELLOG, "TL[%X] created", this); }
CTravelLog::~CTravelLog() { //DestroyList by releasing the root
SAFERELEASE(_pteRoot); TraceMsg(TF_TRAVELLOG, "TL[%X] destroyed ", this); }
HRESULT CTravelLog::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CTravelLog, ITravelLog), // IID_ITravelLog
QITABENT(CTravelLog, ITravelLogEx), // IID_ITravelLogEx
{ 0 }, }; return QISearch(this, qit, riid, ppvObj); }
ULONG CTravelLog::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CTravelLog::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if (0 == cRef) { delete this; } return cRef; }
HRESULT CTravelLog::AddEntry(IUnknown *punk, BOOL fIsLocalAnchor) { ASSERT(punk);
if (SHRestricted2W(REST_NoNavButtons, NULL, 0)) { return S_FALSE; }
TraceMsg(TF_TRAVELLOG, "TL[%X]::AddEntry punk = %X, IsLocal = %s", this, punk, fIsLocalAnchor ? "TRUE" : "FALSE");
CTravelEntry *pte = new CTravelEntry(fIsLocalAnchor); if (pte) { //replace the current with the new
if (_pteCurrent) { CTravelEntry *pteNext = _pteCurrent->GetNext(); if (pteNext) { _cbTotalSize -= pteNext->ListSize(); }
// the list keeps its own ref count, and only needs
// to be modified when passed outside of the list
// setnext will release the current next if necessary
// this will also set pte->prev = pteCurrent
_pteCurrent->SetNext(pte); } else _pteRoot = pte;
_cbTotalSize += pte->Size();
_pteCurrent = pte;
ASSERT(_cbTotalSize == _pteRoot->ListSize()); } TraceMsg(TF_TRAVELLOG, "TL[%X]::AddEntry punk = %X, IsLocal = %d, pte = %X", this, punk, fIsLocalAnchor, pte);
return pte ? S_OK : E_OUTOFMEMORY; }
void CTravelLog::_Prune(void) { // FEATURE: need an increment or something
ASSERT(_cbTotalSize == _pteRoot->ListSize());
while (_cbTotalSize > _cbMaxSize && _pteRoot != _pteCurrent) { CTravelEntry *pte = _pteRoot; _pteRoot = _pteRoot->GetNext();
_cbTotalSize -= pte->Size(); pte->RemoveSelf();
ASSERT(_cbTotalSize == _pteRoot->ListSize()); } }
HRESULT CTravelLog::UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor) { CTravelEntry *pte = _pteUpdate ? _pteUpdate : _pteCurrent;
// this can happen under weird stress conditions, evidently
if (!pte) return E_FAIL;
_cbTotalSize -= pte->Size(); HRESULT hr = pte->Update(punk, fIsLocalAnchor); _cbTotalSize += pte->Size();
ASSERT(_cbTotalSize == _pteRoot->ListSize());
// Debug prints need to be before _Prune() since pte can get freed by _Prune() resulting
// in a crash if pte->Size() is called
TraceMsg(TF_TRAVELLOG, "TL[%X]::UpdateEntry pte->Size() = %d", this, pte->Size()); TraceMsg(TF_TRAVELLOG, "TL[%X]::UpdateEntry punk = %X, IsLocal = %d, hr = %X", this, punk, fIsLocalAnchor, hr); _Prune();
_pteUpdate = NULL;
return hr; }
HRESULT CTravelLog::UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext) { CTravelEntry *pte = _pteUpdate ? _pteUpdate : _pteCurrent;
ASSERT(punk); ASSERT(pte); ASSERT(punkHLBrowseContext);
if (pte) return pte->UpdateExternal(punk, punkHLBrowseContext);
return E_FAIL; }
HRESULT CTravelLog::Travel(IUnknown *punk, int iOffset) { ASSERT(punk); HRESULT hr = E_FAIL;
CTravelEntry *pte;
TraceMsg(TF_TRAVELLOG, "TL[%X]::Travel entered with punk = %X, iOffset = %d", this, punk, iOffset);
if (SUCCEEDED(_FindEntryByOffset(punk, iOffset, &pte))) { #ifdef DEBUG
TCHAR szPath[MAX_PATH]; LPITEMIDLIST pidl; pte->GetPidl(&pidl); SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL); ILFree(pidl); TraceMsgW(TF_TRAVELLOG, "TL[%X]::URL %s", this, szPath); #endif
// we will update where we are before we move away...
// but external navigates dont go through the normal activation
// so we dont want to setup the external to be updated
// _pteUpdate is also what allows us to Revert().
if (!_pteCurrent->IsExternal() && !_pteUpdate) _pteUpdate = _pteCurrent;
_pteCurrent = pte; hr = _pteCurrent->Invoke(punk);
//
// if the entry bails with an error, then we need to reset ourself
// to what we were. right now, the only place this should happen
// is if an Abort was returned from SetPositionCookie
// because somebody aborted during before navigate.
// but i think that any error means that we can legitimately Revert().
//
if (FAILED(hr)) { Revert(); } }
TraceMsg(TF_TRAVELLOG, "TL[%X]::Travel exited with hr = %X", this, hr);
return hr; }
//+---------------------------------------------------------------------------
//
// Method : CTravelLog::TravelToUrl
//
// Interface : ITravelLogEx
//
// Synopsis : Travels to the specified URL in the travel log.
//
//----------------------------------------------------------------------------
HRESULT CTravelLog::TravelToUrl(IUnknown * punk, UINT uiCP, LPOLESTR lpszUrl) { ASSERT(punk); ASSERT(lpszUrl);
HRESULT hr; LPITEMIDLIST pidl; CTravelEntry * pte = NULL; TCHAR szUrl[INTERNET_MAX_URL_LENGTH]; DWORD cchOut = ARRAYSIZE(szUrl); hr = UrlCanonicalize(lpszUrl, szUrl, &cchOut, URL_ESCAPE_SPACES_ONLY); if (SUCCEEDED(hr)) { hr = IEParseDisplayName(uiCP, szUrl, &pidl); if (SUCCEEDED(hr)) { hr = _FindEntryByPidl(punk, pidl, &pte); ILFree(pidl); if (SUCCEEDED(hr)) { // We will update where we are before we move away...
// but external navigates don't go through the normal activation
// so we dont want to setup the external to be updated
// _pteUpdate is also what allows us to Revert().
//
if (!_pteCurrent->IsExternal() && !_pteUpdate) { _pteUpdate = _pteCurrent; }
_pteCurrent = pte; hr = _pteCurrent->Invoke(punk); // If the entry bails with an error, then we need to reset ourself
// to what we were. Right now, the only place this should happen
// is if an Abort was returned from SetPositionCookie
// because somebody aborted during before navigate.
// But i think that any error means that we can legitimately Revert().
//
if (FAILED(hr)) { Revert(); } } } }
TraceMsg(TF_TRAVELLOG, "TL[%X]::TravelToUrl exited with hr = %X", this, hr);
return hr; }
HRESULT CTravelLog::_FindEntryByOffset(IUnknown *punk, int iOffset, CTravelEntry **ppte) { CTravelEntry *pte = _pteCurrent; BOOL fAllowLocalAnchor = TRUE;
if (iOffset < 0) { while (iOffset && pte) { pte = pte->GetPrev(); if (pte && pte->CanInvoke(punk, fAllowLocalAnchor)) { iOffset++; fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor(); }
} } else if (iOffset > 0) { while (iOffset && pte) { pte = pte->GetNext(); if (pte && pte->CanInvoke(punk, fAllowLocalAnchor)) { iOffset--; fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor(); } } }
if (pte) {
*ppte = pte; return S_OK; } return E_FAIL; }
HRESULT CTravelLog::GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte) { HRESULT hr; BOOL fCheckExternal = FALSE; if (iOffset == TLOG_BACKEXTERNAL) { iOffset = TLOG_BACK; fCheckExternal = TRUE; }
if (iOffset == 0) { // APPCOMPAT - going back and fore between external apps is dangerous - zekel 24-JUN-97
// we always fail if the current is external
// this is because word will attempt to navigate us to
// the same url instead of FORE when the user selects
// it from the drop down.
if (_pteCurrent && _pteCurrent->IsExternal()) { hr = E_FAIL; ASSERT(!_pteCurrent->GetPrev()); TraceMsg(TF_TRAVELLOG, "TL[%X]::GetTravelEntry current is External", this); goto Quit; } }
CTravelEntry *pte; hr = _FindEntryByOffset(punk, iOffset, &pte);
//
// If TLOG_BACKEXTERNAL is specified, we return S_OK only if the previous
// entry is external.
//
if (fCheckExternal && SUCCEEDED(hr)) { if (!pte->IsExternal()) { hr = E_FAIL; } TraceMsg(TF_TRAVELLOG, "TL[%X]::GetTravelEntry(BACKEX)", this); }
if (ppte && SUCCEEDED(hr)) { hr = pte->QueryInterface(IID_PPV_ARG(ITravelEntry, ppte)); }
Quit:
TraceMsg(TF_TRAVELLOG, "TL[%X]::GetTravelEntry iOffset = %d, hr = %X", this, iOffset, hr);
return hr; }
//+---------------------------------------------------------------------------
//
// Method : CTravelLog::FindTravelEntry
//
// Synopsis : Finds the travel entry with the specified PIDL and returns
// the ITravelEntry interface of the entry.
//
//----------------------------------------------------------------------------
HRESULT CTravelLog::FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte) { CTravelEntry * pte = _pteRoot; _FindEntryByPidl(punk, pidl, &pte); if (pte) { return pte->QueryInterface(IID_PPV_ARG(ITravelEntry, ppte)); }
*ppte = NULL; return E_FAIL; }
//+---------------------------------------------------------------------------
//
// Method : CTravelLog::_FindEntryByPidl
//
// Synopsis : Finds and returns the travel entry with the specified PIDL.
// This private method returns a CTravelEntry instead of
// an ITravelEntry.
//
//----------------------------------------------------------------------------
HRESULT CTravelLog::_FindEntryByPidl(IUnknown * punk, LPCITEMIDLIST pidl, CTravelEntry ** ppte) { CTravelEntry * pte = _pteRoot; BOOL fAllowLocalAnchor = TRUE;
ASSERT(punk); ASSERT(pidl); ASSERT(ppte); while (pte) { if (pte->CanInvoke(punk, fAllowLocalAnchor) && pte->IsEqual(pidl)) { *ppte = pte; return S_OK; }
fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor();
pte = pte->GetNext(); }
*ppte = NULL; return E_FAIL; }
//+---------------------------------------------------------------------------
//
// Method : CTravelLog::FindEntryByPunk
//
// Interface : ITravelLogEx
//
// Synopsis : Find the entry object given its punk.
//
//----------------------------------------------------------------------------
HRESULT CTravelLog::_FindEntryByPunk(IUnknown * punk, ITravelLogEntry *pteSearch, CTravelEntry ** ppte) { CTravelEntry *pte = _pteRoot; ITravelEntry *pteCur; BOOL fAllowLocalAnchor = TRUE;
ASSERT(ppte);
// check for the current entry.
// often the current entry will fail CanInvoke because it's incomplete at this time.
if (IsSameObject(pteSearch, SAFECAST(_pteCurrent, ITravelEntry*))) { *ppte = _pteCurrent; return S_OK; }
while (pte) { pteCur = SAFECAST(pte, ITravelEntry*); if ((pte->CanInvoke(punk, fAllowLocalAnchor)) && IsSameObject(pteCur, pteSearch)) { *ppte = pte; return S_OK; } fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor(); pte = pte->GetNext(); } *ppte = NULL; return E_FAIL; } //+---------------------------------------------------------------------------
//
// Method : CTravelLog::FindTravelEntryWithUrl
//
// Interface : ITravelLogEx
//
// Synopsis : Finds and returns the travel entry with the specified URL.
//
//----------------------------------------------------------------------------
HRESULT CTravelLog::FindTravelEntryWithUrl(IUnknown * punk, UINT uiCP, LPOLESTR lpszUrl, ITravelEntry ** ppte) { LPITEMIDLIST pidl; HRESULT hr = E_FAIL; ASSERT(punk); ASSERT(lpszUrl); ASSERT(ppte);
if (SUCCEEDED(IEParseDisplayNameWithBCW(uiCP, lpszUrl, NULL, &pidl))) { hr = FindTravelEntry(punk, pidl, ppte); ILFree(pidl); } return hr; }
HRESULT CTravelLog::Clone(ITravelLog **pptl) { CTravelLog *ptl = new CTravelLog(); HRESULT hr = S_OK;
if (ptl && _pteCurrent) { // first set the current pointer
hr = _pteCurrent->Clone(&ptl->_pteCurrent); if (SUCCEEDED(hr)) { ptl->_cbTotalSize = _cbTotalSize; CTravelEntry *pteSrc; CTravelEntry *pteClone, *pteDst = ptl->_pteCurrent; // then we need to loop forward and set each
for (pteSrc = _pteCurrent->GetNext(), pteDst = ptl->_pteCurrent; pteSrc; pteSrc = pteSrc->GetNext()) { ASSERT(pteDst); if (FAILED(pteSrc->Clone(&pteClone))) break;
ASSERT(pteClone); pteDst->SetNext(pteClone); pteDst = pteClone; } //then loop back and set them all
for (pteSrc = _pteCurrent->GetPrev(), pteDst = ptl->_pteCurrent; pteSrc; pteSrc = pteSrc->GetPrev()) { ASSERT(pteDst); if (FAILED(pteSrc->Clone(&pteClone))) break;
ASSERT(pteClone); pteDst->SetPrev(pteClone); pteDst = pteClone; }
// the root is the furthest back we could go
ptl->_pteRoot = pteDst;
} } else hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) { ptl->QueryInterface(IID_PPV_ARG(ITravelLog, pptl)); } else { *pptl = NULL; } if (ptl) ptl->Release();
TraceMsg(TF_TRAVELLOG, "TL[%X]::Clone hr = %x, ptlClone = %X", this, hr, ptl);
return hr; }
// HACKHACK: 3rd parameter used to be idsTemplate, which we would use to grab the
// string template. However, since there's no way the caller can specify the hinst
// of the module in which to look for this resource, this broke in the shdocvw /
// browseui split (callers would pass offsets into browseui.dll; we'd look for them in
// shdocvw.dll). My solution is is to ignore this parameter entirely and assume that:
//
// if iOffset is negative, the caller wants the "back to" text
// else, the caller wants the "forward to" text
//
// tjgreen 14-july-98.
//
HRESULT CTravelLog::GetToolTipText(IUnknown *punk, int iOffset, int, LPWSTR pwzText, DWORD cchText) { TraceMsg(TF_TRAVELLOG, "TL[%X]::ToolTip entering iOffset = %d, ptlClone = %X", this, iOffset); ASSERT(pwzText); ASSERT(cchText);
*pwzText = 0;
CTravelEntry *pte; HRESULT hr = _FindEntryByOffset(punk, iOffset, &pte); if (SUCCEEDED(hr)) { ASSERT(pte);
TCHAR szName[MAX_URL_STRING]; pte->GetDisplayName(szName, ARRAYSIZE(szName), 0);
int idsTemplate = (iOffset < 0) ? IDS_NAVIGATEBACKTO : IDS_NAVIGATEFORWARDTO;
TCHAR szTemplate[80]; if (MLLoadString(idsTemplate, szTemplate, ARRAYSIZE(szTemplate))) { DWORD cchTemplateLen = lstrlen(szTemplate); DWORD cchLen = cchTemplateLen + lstrlen(szName); if (cchLen > cchText) { // so that we don't overflow the pwzText buffer
// review: do we even need this now that we are using StringCchPrintf below?
szName[cchText - cchTemplateLen - 1] = 0; }
StringCchPrintf(pwzText, cchText, szTemplate, szName); } else hr = E_UNEXPECTED; }
TraceMsg(TF_TRAVELLOG, "TL[%X]::ToolTip exiting hr = %X, pwzText = %ls", this, hr, pwzText); return hr; }
HRESULT CTravelLog::InsertMenuEntries(IUnknown *punk, HMENU hmenu, int iIns, int idFirst, int idLast, DWORD dwFlags) { ASSERT(idLast >= idFirst); ASSERT(hmenu); ASSERT(punk);
int cItemsBack = idLast - idFirst + 1; int cItemsFore = 0; CTravelEntry *pte; LONG cAdded = 0;
TraceMsg(TF_TRAVELLOG, "TL[%X]::InsertMenuEntries entered on punk = %X, hmenu = %X, iIns = %d, idRange = %d-%d, flags = %X", this, punk, hmenu, iIns, idFirst, idLast, dwFlags);
ASSERT(cItemsFore >= 0); ASSERT(cItemsBack >= 0);
if (IsFlagSet(dwFlags, TLMENUF_INCLUDECURRENT)) cItemsBack--;
if (IsFlagSet(dwFlags, TLMENUF_BACKANDFORTH)) { cItemsFore = cItemsBack / 2; cItemsBack = cItemsBack - cItemsFore; } else if (IsFlagSet(dwFlags, TLMENUF_FORE)) { cItemsFore = cItemsBack; cItemsBack = 0; }
TCHAR szName[40]; UINT uFlags = MF_STRING | MF_ENABLED | MF_BYPOSITION; while (cItemsFore) { if (SUCCEEDED(_FindEntryByOffset(punk, cItemsFore, &pte))) { pte->GetDisplayName(szName, ARRAYSIZE(szName), TEGDN_FORSYSTEM); ASSERT(*szName); FixAmpersands(szName, ARRAYSIZE(szName)); InsertMenu(hmenu, iIns, uFlags, idLast, szName); cAdded++; TraceMsg(TF_TRAVELLOG, "TL[%X]::IME Fore id = %d, szName = %s", this, idLast, szName); } cItemsFore--; idLast--; }
if (IsFlagSet(dwFlags, TLMENUF_INCLUDECURRENT)) { // clear the name
*szName = 0;
//have to get the title from the actual pbs
LPITEMIDLIST pidl = NULL; IBrowserService *pbs; if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { pbs->GetPidl(&pidl);
WCHAR wzTitle[MAX_PATH]; if (SUCCEEDED(pbs->GetTitle(NULL, wzTitle, ARRAYSIZE(wzTitle)))) { StrCpyN(szName, wzTitle, ARRAYSIZE(szName)); } else if (pidl) { GetUnescapedUrlIfAppropriate(pidl, szName, ARRAYSIZE(szName)); }
pbs->Release(); }
if (!SHIsDisplayable(szName, g_fRunOnFE, g_bRunOnNT5) && pidl) { // Display name isn't system-displayable. Just use the path/url instead.
SHTitleFromPidl(pidl, szName, ARRAYSIZE(szName), FALSE); }
if (!(*szName)) TraceMsg(TF_ERROR, "CTravelLog::InsertMenuEntries -- failed to find title for current entry");
ILFree(pidl);
FixAmpersands(szName, ARRAYSIZE(szName)); InsertMenu(hmenu, iIns, uFlags | (IsFlagSet(dwFlags, TLMENUF_CHECKCURRENT) ? MF_CHECKED : 0), idLast, szName); cAdded++; TraceMsg(TF_TRAVELLOG, "TL[%X]::IME Current id = %d, szName = %s", this, idLast, szName);
idLast--; }
if (IsFlagSet(dwFlags, TLMENUF_BACKANDFORTH)) { // we need to reverse the order of insertion for back
// when both directions are displayed
int i; for (i = 1; i <= cItemsBack; i++, idLast--) { if (SUCCEEDED(_FindEntryByOffset(punk, -i, &pte))) { pte->GetDisplayName(szName, ARRAYSIZE(szName), TEGDN_FORSYSTEM); ASSERT(*szName); FixAmpersands(szName, ARRAYSIZE(szName)); InsertMenu(hmenu, iIns, uFlags, idLast, szName); cAdded++; TraceMsg(TF_TRAVELLOG, "TL[%X]::IME Back id = %d, szName = %s", this, idLast, szName);
} } } else while (cItemsBack) { if (SUCCEEDED(_FindEntryByOffset(punk, -cItemsBack, &pte))) { pte->GetDisplayName(szName, ARRAYSIZE(szName), TEGDN_FORSYSTEM); ASSERT(*szName); FixAmpersands(szName, ARRAYSIZE(szName)); InsertMenu(hmenu, iIns, uFlags, idLast, szName); cAdded++; TraceMsg(TF_TRAVELLOG, "TL[%X]::IME Back id = %d, szName = %s", this, idLast, szName); }
cItemsBack--; idLast--; }
TraceMsg(TF_TRAVELLOG, "TL[%X]::InsertMenuEntries exiting added = %d", this, cAdded); return cAdded ? S_OK : S_FALSE; }
DWORD CTravelLog::CountEntries(IUnknown *punk) { CTravelEntry *pte = _pteRoot; DWORD dw = 0; BOOL fAllowLocalAnchor = TRUE;
while (pte) { if (pte->CanInvoke(punk, fAllowLocalAnchor)) dw++;
fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor();
pte = pte->GetNext(); }
TraceMsg(TF_TRAVELLOG, "TL[%X]::CountEntries count = %d", this, dw); return dw; }
HRESULT CTravelLog::Revert(void) { // this function should only be called when
// we have travelled, and we stop the travel before finishing
if (_pteUpdate) { // trade them back
_pteCurrent = _pteUpdate; _pteUpdate = NULL; return S_OK; } return E_FAIL; }
//
// delete nodes belonging to the frameset pte
//
void CTravelLog::_DeleteFrameSetEntry(IUnknown *punk, CTravelEntry *pte) { ASSERT(pte);
CTravelEntry *ptetmp = pte; BOOL fAllowLocalAnchor = TRUE;
while (ptetmp && ptetmp != _pteCurrent) ptetmp = ptetmp->GetNext();
if (ptetmp) { // entry on left of _pteCurrent , delete on left
do { if (pte == _pteRoot) _pteRoot = pte->GetNext();
ptetmp = pte; pte = pte->GetPrev(); fAllowLocalAnchor = fAllowLocalAnchor && ptetmp->IsLocalAnchor();
_cbTotalSize -= ptetmp->Size(); ptetmp->RemoveSelf();
} while (pte && !(pte->CanInvoke(punk, fAllowLocalAnchor))); } else if (pte) { do { ptetmp = pte; pte = pte->GetNext(); fAllowLocalAnchor = fAllowLocalAnchor && ptetmp->IsLocalAnchor();
_cbTotalSize -= ptetmp->Size(); ptetmp->RemoveSelf();
} while (pte && !(pte->CanInvoke(punk, fAllowLocalAnchor))); } } //+---------------------------------------------------------------------------
//
// Method : CTravelLog::DeleteIndexEntry
//
// Interface : ITravelLogEx
//
// Synopsis : Delete the entry given by index.
//
//----------------------------------------------------------------------------
HRESULT CTravelLog::DeleteIndexEntry(IUnknown *punk, int index) { HRESULT hres = E_FAIL;
CTravelEntry *pte; IBrowserService *pbs; BOOL fAllowLocalAnchor = TRUE; ASSERT(punk);
if (index == 0) // don't delete current entry
return hres;
hres = _FindEntryByOffset(punk, index, &pte); if (SUCCEEDED(hres)) { _DeleteFrameSetEntry(punk, pte);
ASSERT(_cbTotalSize == _pteRoot->ListSize()); if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { pbs->UpdateBackForwardState(); pbs->Release(); } } return hres; }
//+---------------------------------------------------------------------------
//
// Method : CTravelLog::DeleteUrlEntry
//
// Interface : ITravelLogEx
//
// Synopsis : Delete all the entries given by URL. Fails for current entry.
//
//----------------------------------------------------------------------------
HRESULT CTravelLog::DeleteUrlEntry(IUnknown *punk, UINT uiCP, LPOLESTR lpszUrl) { HRESULT hres = E_FAIL; CTravelEntry *pte; IBrowserService *pbs; LPITEMIDLIST pidl; BOOL fAllowLocalAnchor = TRUE; int count = 0; ASSERT(punk); if (SUCCEEDED(IEParseDisplayNameWithBCW(uiCP, lpszUrl, NULL, &pidl))) { // delete only if different from current
if (!_pteCurrent->IsEqual(pidl)) { hres = S_OK; while(SUCCEEDED(_FindEntryByPidl(punk, pidl, &pte))) { _DeleteFrameSetEntry(punk, pte); count++; ASSERT(_cbTotalSize == _pteRoot->ListSize()); } }
ILFree(pidl);
if (count && SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { pbs->UpdateBackForwardState(); pbs->Release(); } } return hres; }
//+---------------------------------------------------------------------------
//
// Method : CTravelLog::DeleteEntry
//
// Interface : ITravelLogEx
//
// Synopsis : Delete the entries given by punk. Fails for current entry.
//
//----------------------------------------------------------------------------
HRESULT CTravelLog::DeleteEntry(IUnknown *punk, ITravelLogEntry *ptleDelete) { HRESULT hres;
CTravelEntry *pte; BOOL fAllowLocalAnchor = TRUE; IBrowserService *pbs;
ASSERT(punk);
hres = _FindEntryByPunk(punk, ptleDelete, &pte); if (SUCCEEDED(hres) && pte != _pteCurrent) // don't remove current
{ _DeleteFrameSetEntry(punk, pte);
ASSERT(_cbTotalSize == _pteRoot->ListSize()); if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { pbs->UpdateBackForwardState(); pbs->Release(); } } else { hres = E_FAIL; } return hres; }
//+---------------------------------------------------------------------------
//
// Method : CTravelLog::CountEntryNodes
//
// Synopsis : Counts Back/Forward entries including the current one
// as given by dwFlags
//
//----------------------------------------------------------------------------
HRESULT CTravelLog::CountEntryNodes(IUnknown *punk, DWORD dwFlags, DWORD *pdwCount) { CTravelEntry *pte; BOOL fAllowLocalAnchor = TRUE; ASSERT(punk); DWORD dwCount = 0; if (!_pteCurrent) { *pdwCount = 0; return S_OK; }
if (IsFlagSet(dwFlags, TLMENUF_BACK)) { pte = _pteRoot; while (pte != _pteCurrent) { if (pte->CanInvoke(punk, fAllowLocalAnchor)) { dwCount++; fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor(); } pte = pte->GetNext(); } }
if (IsFlagSet(dwFlags, TLMENUF_INCLUDECURRENT)) { if (_pteCurrent->CanInvoke(punk, fAllowLocalAnchor)) { dwCount++; fAllowLocalAnchor = fAllowLocalAnchor && _pteCurrent->IsLocalAnchor(); } } if (IsFlagSet(dwFlags, TLMENUF_FORE)) { pte = _pteCurrent->GetNext(); while (pte) { if (pte->CanInvoke(punk, fAllowLocalAnchor)) { dwCount++; fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor(); } pte = pte->GetNext(); } }
*pdwCount = dwCount; TraceMsg(TF_TRAVELLOG, "TL[%X]::CountEntryNodes count = %d", this, *pdwCount); return S_OK; }
//+---------------------------------------------------------------------------
//
// Method : CTravelLog::CreateEnumEntry
//
// Synopsis : Returns an enumerator object for the back/fore travel entries
// as selected by the dwFlags option
//
//----------------------------------------------------------------------------
HRESULT CTravelLog::CreateEnumEntry(IUnknown *punk, IEnumTravelLogEntry **ppEnum, DWORD dwFlags) { ASSERT(punk); ASSERT(ppEnum); *ppEnum = 0;
HRESULT hr = E_OUTOFMEMORY; CEnumEntry *penum = new CEnumEntry(); if (penum) { penum->Init(this, punk, 0, dwFlags); *ppEnum = SAFECAST(penum, IEnumTravelLogEntry *); hr = S_OK; } return hr; }
//+---------------------------------------------------------------------------
//
// Method : CTravelLogEx::InsertEntry
//
// Synopsis : Inserts an entry into the specified position in the
// travellog and calls Update with the given IUnknown.
//
//----------------------------------------------------------------------------
HRESULT CTravelLog::InsertEntry(IUnknown *punkBrowser, ITravelLogEntry *pteRelativeTo, BOOL fPrepend, IUnknown* punkTLClient, ITravelLogEntry **ppEntry) { TraceMsg(TF_TRAVELLOG, "TL[%X]::InsertEntry", this); ASSERT(punkBrowser);
CTravelEntry * pteRelative; _FindEntryByPunk(punkBrowser, pteRelativeTo, &pteRelative);
if (!pteRelative) pteRelative = _pteCurrent;
CTravelEntry *pte = new CTravelEntry(FALSE); if (!pte) return E_OUTOFMEMORY;
if (fPrepend) { // keep relative alive as it's reconnected
pteRelative->AddRef(); pte->SetPrev(pteRelative->GetPrev()); pteRelative->SetPrev(pte); if (pteRelative == _pteRoot) { _pteRoot = pte; } } else { CTravelEntry * pteNext = pteRelative->GetNext(); if (pteNext) pteNext->AddRef(); pte->SetNext(pteNext); pteRelative->SetNext(pte); }
// update will fill in all the data from the passed in TL Client
HRESULT hres = pte->Update(punkTLClient, FALSE);
_cbTotalSize += pte->Size(); ASSERT(_cbTotalSize == _pteRoot->ListSize());
IBrowserService *pbs; if (SUCCEEDED(punkBrowser->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { pbs->UpdateBackForwardState(); pbs->Release(); hres = S_OK; }
// return the ITLEntry for the new entry
if (SUCCEEDED(hres) && ppEntry) { hres = pte->QueryInterface(IID_PPV_ARG(ITravelLogEntry, ppEntry)); }
return hres; }
//+---------------------------------------------------------------------------
//
// Method : CTravelLog::TravelToEntry
//
// Synopsis : Travels directly to the specified entry.
// Invoke cannot be called directly due to update strangeness.
//
//----------------------------------------------------------------------------
HRESULT CTravelLog::TravelToEntry( IUnknown *punkBrowser, ITravelLogEntry *pteDestination) { HRESULT hr = E_FAIL; CTravelEntry *pte = NULL;
ASSERT(punkBrowser); ASSERT(pteDestination);
_FindEntryByPunk(punkBrowser, pteDestination , &pte); if (pte) { if (!_pteCurrent->IsExternal() && !_pteUpdate) _pteUpdate = _pteCurrent;
_pteCurrent = pte;
hr = pte->Invoke(punkBrowser);
if (FAILED(hr)) { Revert(); } }
return hr; }
HRESULT CreateTravelLog(ITravelLog **pptl) { HRESULT hres; CTravelLog *ptl = new CTravelLog(); if (ptl) { hres = ptl->QueryInterface(IID_PPV_ARG(ITravelLog, pptl)); ptl->Release(); } else { *pptl = NULL; hres = E_OUTOFMEMORY; } return hres; }
CEnumEntry::CEnumEntry() :_cRef(1) { ASSERT(!_ptl); ASSERT(!_punk);
TraceMsg(TF_TRAVELLOG, "EET[%X] created ", this); }
CEnumEntry::~CEnumEntry() { SAFERELEASE(_ptl); SAFERELEASE(_punk); TraceMsg(TF_TRAVELLOG, "EET[%X] destroyed ", this); }
HRESULT CEnumEntry::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CEnumEntry, IEnumTravelLogEntry), { 0 }, }; return QISearch(this, qit, riid, ppvObj); }
ULONG CEnumEntry::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CEnumEntry::Release() { ASSERT(0 != _cRef); ULONG cRef = InterlockedDecrement(&_cRef); if (0 == cRef) { delete this; } return cRef; }
void CEnumEntry::Init(CTravelLog *ptl, IUnknown *punk, DWORD dwOffset, DWORD dwFlags) { ASSERT(ptl); ASSERT(punk); _ptl = ptl; _dwFlags = dwFlags; _punk = punk; _dwOffset = dwOffset; _lStart = 0; _ptl->AddRef(); _punk->AddRef(); SetBase(); }
void CEnumEntry::SetBase() { ITravelEntry *ptetmp;
// the start is always computed relative to the current entry
if (IsFlagSet(_dwFlags, TLEF_RELATIVE_FORE|TLEF_RELATIVE_BACK)) { _lStart = -1; while (SUCCEEDED(_ptl->GetTravelEntry(_punk, _lStart, &ptetmp))) { _lStart--; ptetmp->Release(); } _lStart++; } else if (!IsFlagSet(_dwFlags, TLEF_RELATIVE_INCLUDE_CURRENT)) _lStart = IsFlagSet(_dwFlags, TLEF_RELATIVE_BACK) ? -1: 1; }
HRESULT CEnumEntry::Reset() { _dwOffset = 0;
// base changes when add/delete entries
SetBase(); return S_OK; }
HRESULT CEnumEntry::Skip(ULONG cElt) { HRESULT hres; ITravelEntry *pte; ULONG uCount; LONG lIndex; BOOL fToRight = IsFlagSet(_dwFlags, TLEF_RELATIVE_FORE); BOOL fIncludeCurrent = IsFlagSet(_dwFlags, TLEF_RELATIVE_INCLUDE_CURRENT);
for (uCount = 0; uCount < cElt; uCount++) { lIndex = fToRight ? _lStart + _dwOffset : _lStart - _dwOffset; if (lIndex || fIncludeCurrent) { if (SUCCEEDED(hres = _ptl->GetTravelEntry(_punk, lIndex, &pte))) { _dwOffset++; pte->Release(); } else break; } else { _dwOffset++; uCount--; } } if (uCount != cElt) hres = S_FALSE;
return hres; }
HRESULT CEnumEntry::Next(ULONG cElt, ITravelLogEntry **rgpte2, ULONG *pcEltFetched) { HRESULT hres = S_OK; ULONG uCount = 0; ITravelEntry *pte; LONG lIndex; BOOL fToRight; BOOL fIncludeCurrent; fToRight = IsFlagSet(_dwFlags, TLEF_RELATIVE_FORE); fIncludeCurrent = IsFlagSet(_dwFlags, TLEF_RELATIVE_INCLUDE_CURRENT); if (pcEltFetched) *pcEltFetched = 0; for (uCount = 0; uCount < cElt; uCount++) { lIndex = fToRight ? _lStart + _dwOffset : _lStart - _dwOffset; if (lIndex || fIncludeCurrent) { hres = _ptl->GetTravelEntry(_punk, lIndex, &pte); if (SUCCEEDED(hres)) { _dwOffset++; pte->QueryInterface(IID_PPV_ARG(ITravelLogEntry, &rgpte2[uCount])); pte->Release(); } else break; } else { _dwOffset++; uCount--; } } if (pcEltFetched ) *pcEltFetched = uCount;
if (uCount != cElt) { hres = S_FALSE; for(;uCount < cElt; uCount++) rgpte2[uCount] = 0; } return hres; }
STDMETHODIMP CEnumEntry::Clone(IEnumTravelLogEntry **ppEnum) { HRESULT hres = E_OUTOFMEMORY; CEnumEntry *penum;
ASSERT(ppEnum); *ppEnum = 0; penum = new CEnumEntry();
if (penum) { penum->Init(_ptl, _punk, _dwOffset, _dwFlags); *ppEnum = SAFECAST(penum, IEnumTravelLogEntry*); hres = S_OK; }
return hres; }
// Helper object for creating new travel entries
class CPublicTravelLogCreateHelper : public ITravelLogClient, IPersistHistory { public: // *** IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef() ; STDMETHODIMP_(ULONG) Release();
// ITravelLogClient methods
STDMETHODIMP FindWindowByIndex(DWORD dwID, IUnknown **ppunk); STDMETHODIMP GetWindowData(WINDOWDATA *pwindata); STDMETHODIMP LoadHistoryPosition(LPOLESTR pszUrlLocation, DWORD dwCookie) { return SetPositionCookie(dwCookie); } // IPersist methods. (dummy)
STDMETHODIMP GetClassID(CLSID *pClassID) { ASSERT(FALSE); return E_NOTIMPL; } // IPersistHistory methods. (dummy)
// These should never be called, but Update QI's the client to see if it supports IPH.
STDMETHODIMP LoadHistory(IStream *pStream, IBindCtx *pbc) { return E_NOTIMPL; }
STDMETHODIMP SaveHistory(IStream *pStream) { ASSERT(FALSE); return S_OK; }
STDMETHODIMP SetPositionCookie(DWORD dwPositioncookie) { return E_NOTIMPL; } STDMETHODIMP GetPositionCookie(DWORD *pdwPositioncookie) { return E_NOTIMPL; }
CPublicTravelLogCreateHelper(IBrowserService *pbs, LPCOLESTR pszUrl, LPCOLESTR pszTitle);
protected: ~CPublicTravelLogCreateHelper();
LONG _cRef; IBrowserService *_pbs; LPOLESTR _pszUrl; LPOLESTR _pszTitle; };
CPublicTravelLogCreateHelper::CPublicTravelLogCreateHelper(IBrowserService *pbs, LPCOLESTR pszUrl, LPCOLESTR pszTitle) : _pbs(pbs), _cRef(1) { ASSERT(_pbs); ASSERT(!_pszUrl); ASSERT(!_pszTitle); ASSERT(pszUrl); ASSERT(pszTitle);
if (_pbs) _pbs->AddRef(); if (pszUrl) { SHStrDup(pszUrl, &_pszUrl); } if (pszTitle) { SHStrDup(pszTitle, &_pszTitle); } TraceMsg(TF_TRAVELLOG, "TPLCH[%X] created", this); }
CPublicTravelLogCreateHelper::~CPublicTravelLogCreateHelper() { SAFERELEASE(_pbs); CoTaskMemFree(_pszUrl); CoTaskMemFree(_pszTitle);
TraceMsg(TF_TRAVELLOG, "TPLCH[%X] destroyed ", this); }
HRESULT CPublicTravelLogCreateHelper ::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CPublicTravelLogCreateHelper , ITravelLogClient), QITABENT(CPublicTravelLogCreateHelper , IPersistHistory), QITABENT(CPublicTravelLogCreateHelper , IPersist), { 0 }, }; return QISearch(this, qit, riid, ppvObj); }
ULONG CPublicTravelLogCreateHelper ::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CPublicTravelLogCreateHelper ::Release() { ASSERT(0 != _cRef); ULONG cRef = InterlockedDecrement(&_cRef); if (0 == cRef) { delete this; } return cRef; }
// ITravelLogClient::FindWindowByIndex
HRESULT CPublicTravelLogCreateHelper::FindWindowByIndex(DWORD dwID, IUnknown **ppunk) { *ppunk = NULL; return E_NOTIMPL; }
// ITravelLogClient::GetWindowData
// turns around and talks to the browser
HRESULT CPublicTravelLogCreateHelper::GetWindowData(WINDOWDATA *pwindata) { ITravelLogClient2 *pcli; HRESULT hres = _pbs->QueryInterface(IID_PPV_ARG(ITravelLogClient2, &pcli)); if (SUCCEEDED(hres)) hres = pcli->GetDummyWindowData(_pszUrl, _pszTitle, pwindata);
if (pcli) pcli->Release();
return SUCCEEDED(hres) ? S_OK : E_FAIL; }
// Implements the publicly exposed interface ITravelLogStg (can be QS'd for from top browser)
class CPublicTravelLog : public ITravelLogStg { public: // *** IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef() ; STDMETHODIMP_(ULONG) Release();
// *** ITravelLogStg specific methods
STDMETHODIMP CreateEntry(LPCOLESTR pszUrl, LPCOLESTR pszTitle, ITravelLogEntry *ptleRelativeTo, BOOL fPrepend, ITravelLogEntry **pptle); STDMETHODIMP TravelTo(ITravelLogEntry *ptle); STDMETHODIMP EnumEntries(TLENUMF flags, IEnumTravelLogEntry **ppenum); STDMETHODIMP FindEntries(TLENUMF flags, LPCOLESTR pszUrl, IEnumTravelLogEntry **ppenum); STDMETHODIMP GetCount(TLENUMF flags, DWORD *pcEntries); STDMETHODIMP RemoveEntry(ITravelLogEntry *ptle); STDMETHODIMP GetRelativeEntry(int iOffset, ITravelLogEntry **ptle);
CPublicTravelLog(IBrowserService *pbs, ITravelLogEx *ptlx);
protected: ~CPublicTravelLog();
LONG _cRef; IBrowserService *_pbs; ITravelLogEx *_ptlx; };
CPublicTravelLog::CPublicTravelLog(IBrowserService *pbs, ITravelLogEx *ptlx) : _pbs(pbs), _ptlx(ptlx), _cRef(1) { ASSERT(pbs); ASSERT(ptlx);
// We don't addref _pbs because we are always contained within the browser serivce,
// so avoid the circular ref.
if (_ptlx) _ptlx->AddRef(); TraceMsg(TF_TRAVELLOG, "TLP[%X] created", this); }
CPublicTravelLog::~CPublicTravelLog() { // We don't need to release _pbs because we are always contained within the browser serivce,
// so we didn't addref to avoid the circular ref so don't release
SAFERELEASE(_ptlx); TraceMsg(TF_TRAVELLOG, "TLP[%X] destroyed ", this); }
HRESULT CPublicTravelLog::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CPublicTravelLog, ITravelLogStg), // IID_ITravelLogStg
{ 0 }, }; return QISearch(this, qit, riid, ppvObj); }
ULONG CPublicTravelLog::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CPublicTravelLog::Release() { ASSERT(0 != _cRef); ULONG cRef = InterlockedDecrement(&_cRef); if (0 == cRef) { delete this; } return cRef; }
//+---------------------------------------------------------------------------
//
// Method : CPublicTravelLog::CreateEntry
//
// Interface : ITravelLogStg
//
// Synopsis : Insert a new dummy entry.
// Creates an entry in the travel log and passes CPTHCEHelper
// as travel log client; that gets called back and fills in the
// data from the browser.
//
//----------------------------------------------------------------------------
HRESULT CPublicTravelLog::CreateEntry(LPCOLESTR pszUrl, LPCOLESTR pszTitle, ITravelLogEntry *ptleRelativeTo, BOOL fPrepend, ITravelLogEntry **pptle) { HRESULT hres = E_FAIL; CPublicTravelLogCreateHelper * ptlch; ITravelLogClient *ptlc;
ptlch = new CPublicTravelLogCreateHelper(_pbs, pszUrl, pszTitle); if (!ptlch) return E_OUTOFMEMORY; ptlc = SAFECAST(ptlch, ITravelLogClient *); if (ptlc) { // Create TLogEntry and have it get its data from the helper.
hres = _ptlx->InsertEntry(_pbs, ptleRelativeTo, fPrepend, ptlc, pptle); }
ptlc->Release();
return hres; }
HRESULT CPublicTravelLog::TravelTo(ITravelLogEntry *ptle) { if (ptle) return _ptlx->TravelToEntry(_pbs, ptle); else return E_POINTER; }
//+---------------------------------------------------------------------------
//
// Method : CPublicTravelLog::EnumEntries
//
// Interface : ITravelLogStg
//
// Synopsis : Get an enumerators for specific entries given by flags.
// Flags should match with those used by ITravelLogEx!
//
//----------------------------------------------------------------------------
HRESULT CPublicTravelLog::EnumEntries(TLENUMF flags, IEnumTravelLogEntry **ppenum) { return _ptlx->CreateEnumEntry(_pbs, ppenum, flags); }
//+---------------------------------------------------------------------------
//
// Method : CPublicTravelLog::FindEntries
//
// Interface : ITravelLogStg
//
// Synopsis : Allow to retrieve duplicate entries.
// Flags should match with those used by ITravelLogEx!
//
//----------------------------------------------------------------------------
HRESULT CPublicTravelLog::FindEntries(TLENUMF flags, LPCOLESTR pszUrl, IEnumTravelLogEntry **ppenum) { return E_NOTIMPL; }
//+---------------------------------------------------------------------------
//
// Method : CPublicTravelLog::GetCount
//
// Interface : ITravelLogStg
//
// Synopsis : Public methods to get ITravelLogEx count.
// Flags should match with those used by ITravelLogEx!
//
//----------------------------------------------------------------------------
HRESULT CPublicTravelLog::GetCount(TLENUMF flags, DWORD *pcEntries) { return _ptlx->CountEntryNodes(_pbs, flags, pcEntries); }
//+---------------------------------------------------------------------------
//
// Method : CPublicTravelLog::RemoveEntry
//
// Interface : ITravelLogStg
//
// Synopsis : Delete the entry ant its frameset.
//
//----------------------------------------------------------------------------
HRESULT CPublicTravelLog::RemoveEntry(ITravelLogEntry *ptle) { HRESULT hr = E_FAIL;
if (ptle) hr = _ptlx->DeleteEntry(_pbs, ptle); return hr; }
HRESULT CPublicTravelLog::GetRelativeEntry(int iOffset, ITravelLogEntry **pptle) { HRESULT hr = E_FAIL; ITravelEntry* pte; ITravelLog* ptl;
if (SUCCEEDED(_ptlx->QueryInterface(IID_PPV_ARG(ITravelLog, &ptl)))) { hr = ptl->GetTravelEntry(_pbs, iOffset, &pte); if (SUCCEEDED(hr) && pte) { hr = pte->QueryInterface(IID_PPV_ARG(ITravelLogEntry, pptle)); pte->Release(); } ptl->Release(); }
return hr; }
// public method used by the browser to create us
HRESULT CreatePublicTravelLog(IBrowserService *pbs, ITravelLogEx* ptlx, ITravelLogStg **pptlstg) { HRESULT hres; CPublicTravelLog *ptlp = new CPublicTravelLog(pbs, ptlx); if (ptlp) { hres = ptlp->QueryInterface(IID_PPV_ARG(ITravelLogStg, pptlstg)); ptlp->Release(); } else { *pptlstg = NULL; hres = E_OUTOFMEMORY; } return hres; }
|