|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1999 - 1999
//
// File: histlist.cpp
//
//--------------------------------------------------------------------------
#include "stdafx.h"
#include "histlist.h"
#include "cstr.h"
#include "amcmsgid.h"
#include "websnk.h"
#include "webctrl.h"
//############################################################################
//############################################################################
//
// Traces
//
//############################################################################
//############################################################################
#ifdef DBG
CTraceTag tagHistory(TEXT("History"), TEXT("History"));
LPCTSTR SzHistoryEntryType(CHistoryEntry &entry) { if(entry.IsListEntry()) return TEXT("ListView");
else if(entry.IsOCXEntry()) return TEXT("OCXView ");
else if(entry.IsWebEntry()) return TEXT("WebView ");
else ASSERT(0 && "Should not come here"); return TEXT("Illegal entry"); }
#define TraceHistory(Name, iter) \
{ \ USES_CONVERSION; \ Trace(tagHistory, TEXT("%s hNode = %d, %s, viewMode = %d, strOCX = \"%s\" iterator = %d "), \ Name, iter->hnode, SzHistoryEntryType(*iter), iter->viewMode, \ TEXT(""), (LPARAM) &*iter); \ }
#else // DBG
#define TraceHistory(Name, iter)
#endif // DBG
//############################################################################
//############################################################################
//
// Implementation of class CHistoryEntry
//
//############################################################################
//############################################################################
bool CHistoryEntry::operator == (const CHistoryEntry &other) const { if( hnode != other.hnode) return false;
if( guidTaskpad != other.guidTaskpad) return false;
if(resultViewType != other.resultViewType) // NOTE: implement operator == for CResultViewType.
return false;
return true; }
bool CHistoryEntry::operator != (const CHistoryEntry &other) const { return !operator == (other); }
//############################################################################
//############################################################################
//
// Implementation of class CHistoryList
//
//############################################################################
//############################################################################
CHistoryList::CHistoryList(CAMCView* pAMCView) : m_bBrowserBackEnabled(false), m_bBrowserForwardEnabled(false), m_pWebViewCtrl(NULL), m_bPageBreak(false), m_bWithin_CHistoryList_Back(false), m_bWithin_CHistoryList_Forward(false) { m_pView = pAMCView; m_iterCurrent = m_entries.begin(); m_navState = MMC_HISTORY_READY; // not busy
} CHistoryList::~CHistoryList() { }
SC CHistoryList::ScOnPageBreak() { DECLARE_SC(sc, TEXT("CHistoryList::ScOnPageBreak"));
// handle recursion
if(MMC_HISTORY_PAGE_BREAK == m_navState) { Trace(tagHistory, _T("OnPageBreak() - while inserting pagebreak"));
m_navState = MMC_HISTORY_READY; return sc; }
bool bHandled = false; if(m_bCurrentStateIsForward) { Trace(tagHistory, _T("OnPageBreak() - while going Forward")); Forward(bHandled, false); } else { Trace(tagHistory, _T("OnPageBreak() - while going Back")); Back(bHandled, false); }
if(!bHandled) { Trace(tagHistory, _T("OnPageBreak() - unhandled, passing back to web browser")); m_bCurrentStateIsForward ? GetWebViewCtrl()->Forward() : GetWebViewCtrl()->Back(); }
return sc; }
void CHistoryList::OnPageBreakStateChange(bool bPageBreak) { m_bPageBreak = bPageBreak; return; }
/*+-------------------------------------------------------------------------*
* * CHistoryList::OnBrowserStateChange * * PURPOSE: Callback that receives events from the IE control that the * forward/back button needs to be enabled/disabled. A * combination of this information with any non-HTML states in the * history list is used to enable/disable the actual UI. * * PARAMETERS: * bool bForward : * bool bEnable : * * RETURNS: * void * *+-------------------------------------------------------------------------*/ void CHistoryList::OnBrowserStateChange(bool bEnableForward, bool bEnableBack) { #if DBG
CStr strTrace; strTrace.Format(_T("OnBrowserStateChange() - bEnableForward = %s, bEnableBack = %s"), bEnableForward ? _T("true") : _T("false"), bEnableBack ? _T("true") : _T("false")); Trace(tagHistory, strTrace); #endif
// handle the forward case.
if(m_bBrowserForwardEnabled && !bEnableForward && !m_bPageBreak) { // the button was originally enabled but is now disabled.
// This means that the user branched forward. So we need to throw away
// any history ahead of the present time.
if(m_iterCurrent != m_entries.end()) { iterator iterTemp = m_iterCurrent; TraceHistory(TEXT("CHistoryList::Deleting all subsequent entries after"), iterTemp); ++iterTemp; m_entries.erase(iterTemp, m_entries.end()); } }
m_bBrowserForwardEnabled = bEnableForward; m_bBrowserBackEnabled = bEnableBack;
MaintainWebBar(); }
/*+-------------------------------------------------------------------------*
* * CHistoryList::IsFirst * * PURPOSE: * * RETURNS: * BOOL: TRUE if we should not light up the "Back" button. * *+-------------------------------------------------------------------------*/ BOOL CHistoryList::IsFirst() { return (m_iterCurrent == m_entries.begin()); }
/*+-------------------------------------------------------------------------*
* * CHistoryList::IsLast * * PURPOSE: * * RETURNS: * BOOL : TRUE if we should not light up the "Forward" button * *+-------------------------------------------------------------------------*/ BOOL CHistoryList::IsLast() { // see notes above
if(m_iterCurrent == m_entries.end()) return TRUE;
// find next unique entry, if any
iterator iter = m_iterCurrent; ++iter; // this must exist, we've already taken care of the end case.
return(iter == m_entries.end()); }
SC CHistoryList::ScDoPageBreak() { DECLARE_SC(sc, TEXT("CHistoryList::ScDoPageBreak"));
sc = ScCheckPointers(GetWebViewCtrl()); if(sc) return sc;
Trace(tagHistory, _T("ScDoPageBreak()"));
// navigate to the "break" page.
m_navState = MMC_HISTORY_PAGE_BREAK;
CStr strResultPane; sc = ScGetPageBreakURL (strResultPane); if (sc) return (sc);
GetWebViewCtrl()->Navigate(strResultPane, NULL);
//wait for the navigate to complete.
while (1) { READYSTATE state; sc = GetWebViewCtrl()->ScGetReadyState (state); if (sc) return (sc);
if ((state == READYSTATE_COMPLETE) || (state == READYSTATE_LOADED)) break;
MSG msg;
if(!GetMessage( &msg, NULL, 0, 0 )) // the WM_QUIT message.
{ PostQuitMessage (msg.wParam); return sc; }
// If it is view close message make sure it gets posted (async) again.
if ( (msg.message == WM_SYSCOMMAND) && (msg.wParam == SC_CLOSE)) { // Make sure the message is intended for this view.
CWnd *pWnd = m_pView->GetParent(); if ( msg.hwnd == pWnd->GetSafeHwnd()) { // DeleteView does PostMessage(WM_SYSCOMMAND, SC_CLOSE)
m_pView->DeleteView(); return sc; } }
TranslateMessage( &msg ); DispatchMessage( &msg ); }
// m_navState = MMC_HISTORY_READY; // don't set the here. Will be set in OnPageBreak().
return sc; }
/*+-------------------------------------------------------------------------*
* * CHistoryList::ScAddEntry * * PURPOSE: Adds a history entry * * PARAMETERS: * CResultViewType & rvt : * int viewMode: The list view mode (large icon, etc) * GUID & guidTaskpad : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CHistoryList::ScAddEntry(CResultViewType &rvt, int viewMode, GUID &guidTaskpad) { DECLARE_SC(sc, TEXT("CHistoryList::ScAddEntry"));
if(m_navState != MMC_HISTORY_READY) { #ifdef DBG
CHistoryEntry entry; entry.viewMode = viewMode; entry.resultViewType = rvt; entry.guidTaskpad = guidTaskpad; TraceHistory(TEXT("CHistoryList::Busy-RejectEntry"), (&entry)); #endif //DBG
MaintainWebBar(); return sc; }
BOOL bIsWebEntry = rvt.HasWebBrowser();
// must be in the MMC_HISTORY_READY state at this point, ie not busy.
// figure out current node
HNODE hnode = m_pView->GetSelectedNode(); if(hnode == NULL) return sc; // not initialized yet
/* if selection change includes the web page (either as 'from' or 'to' node)
* we need to perform proper separation of the webentries by inserting the pagebreaks. * This is to be done to ensure 2 goals: * - to detect when navigation should leave the IE history and use MMC history navigation * - to ensure we always leave IE only after navigating to a pagebreak - to stop the * scripts on the page as soon as we hide it. to achieve that we need pagebreaks * before every web page and after every web page. * * to do so we need one of the following: * * 1. Add a pagebreak (used when selection changes from the web page to non-web view) * 2. Add a pagebreak and navigate * (a. when selection changes from web page to another web page) * (b. when selection changes from non-web view to the webpage * and it is the first web page in the history) * 3. Navigate only. ( when selection changes from non-web view to the * webpage [except #2.b case] - pagebreak had to be added when leaving the * previous web page) * * inverting the said will result in: * - add a pagebreak if : * C1: web page is a 'from' node (#1. and #2.a.) * C2: web page is a 'to' node * && no previous web pages * && 'from' node is a non-web view (#2.b) * - navigate to web page if: * C3: "to' node is the web page */
// see if we were in the web before this
// Note: both following variables may be false (in case it there are no entries)
bool bPreviousPageWasWeb = (m_entries.size() != 0) && m_iterCurrent->IsWebEntry(); bool bPreviousPageWasNonWeb = (m_entries.size() != 0) && !bPreviousPageWasWeb;
// see if we need a pagebreak
bool bNeedAPageBreak = false; if ( bPreviousPageWasWeb ) { // condition C1 in the comment above
bNeedAPageBreak = true; } else if ( bIsWebEntry && !PreviousWebPagesExist() && bPreviousPageWasNonWeb ) { // condition C2 in the comment above
bNeedAPageBreak = true; }
// conditions C1 || C2 || C3 in the comment above
if (bIsWebEntry || bNeedAPageBreak) { USES_CONVERSION; LPCTSTR szURL = bIsWebEntry ? (OLE2CT( rvt.GetURL() )) : NULL; sc = m_pView->ScAddPageBreakAndNavigate (bNeedAPageBreak, bIsWebEntry, szURL); if(sc) return sc; }
DeleteSubsequentEntries();
// add an entry to the end of the list.
CHistoryEntry entry; ZeroMemory(&entry, sizeof(entry)); m_entries.push_back(entry); m_iterCurrent = m_entries.end(); --m_iterCurrent; // points to the newly inserted item.
m_iterCurrent->viewMode = viewMode; m_iterCurrent->guidTaskpad = guidTaskpad; m_iterCurrent->hnode = hnode; m_iterCurrent->resultViewType = rvt;
TraceHistory(TEXT("CHistoryList::AddEntry"), m_iterCurrent);
Compact(); MaintainWebBar();
return sc; }
/*+-------------------------------------------------------------------------*
* * CHistoryList::DeleteSubsequentEntries * * PURPOSE: When a new entry is inserted, all subsequent entries need to be * deleted, because a new branch has been taken. * * RETURNS: * void * *+-------------------------------------------------------------------------*/ void CHistoryList::DeleteSubsequentEntries() { if(m_iterCurrent == m_entries.end()) return; // nothing to do.
iterator iterTemp = m_iterCurrent; ++iterTemp;
while(iterTemp != m_entries.end()) { iterator iterNext = iterTemp; ++iterNext; // point to the next element.
TraceHistory(TEXT("CHistoryList::DeleteSubsequentEntries"), iterTemp);
m_entries.erase(iterTemp); iterTemp = iterNext; }
// the current entry must be the last at this stage.
#ifdef DBG
{ iterator iterTemp = m_iterCurrent; ++iterTemp; ASSERT(iterTemp == m_entries.end()); } #endif
}
/*+-------------------------------------------------------------------------*
* * CHistoryList::Back * * PURPOSE: * * PARAMETERS: * bool & bHandled : * * RETURNS: * HRESULT * *+-------------------------------------------------------------------------*/ HRESULT CHistoryList::Back(bool &bHandled, bool bUseBrowserHistory) { Trace(tagHistory, TEXT("Back()"));
// change the state to indicate we are navigating back.
// and assure it is reset on function exit
m_bWithin_CHistoryList_Back = true; CAutoAssignOnExit<bool, false> auto_reset( m_bWithin_CHistoryList_Back );
// if we're in browser mode AND
// if the back button is enabled by the browser use browser history.
m_bCurrentStateIsForward = false; if( (m_iterCurrent->IsWebEntry()) && bUseBrowserHistory) { if(m_bBrowserBackEnabled) { Trace(tagHistory, TEXT("Back() web entry - not handling")); bHandled = false; return S_OK; } }
bHandled = true;
// BOGUS assert - amcview calls Back when ALT <- is pressed
// regardless of the state of the button.
//ASSERT (m_iterCurrent != m_entries.begin());
if(m_iterCurrent == m_entries.begin()) return S_FALSE;
--m_iterCurrent;
HRESULT hr = ExecuteCurrent(); if(FAILED(hr)) return hr;
if(m_iterCurrent->IsWebEntry()) { if(m_bPageBreak) // if we're at a page break, go past it.
{ Trace(tagHistory, TEXT("Back() - stepped on the pagebreak")); bHandled = false; // this tells the caller to use the Browser's Back button.
} }
return hr; }
/*+-------------------------------------------------------------------------*
* * CHistoryList::Forward * * PURPOSE: * * PARAMETERS: * bool & bHandled : * * RETURNS: * HRESULT * *+-------------------------------------------------------------------------*/ HRESULT CHistoryList::Forward(bool &bHandled, bool bUseBrowserHistory) { // change the state to indicate we are navigating forward.
// and assure it is reset on function exit
m_bWithin_CHistoryList_Forward = true; CAutoAssignOnExit<bool, false> auto_reset( m_bWithin_CHistoryList_Forward );
// if we're in browser mode AND
// if the forward button is enabled by the browser use browser history.
m_bCurrentStateIsForward = true; if( (m_iterCurrent->IsWebEntry()) && bUseBrowserHistory) { if(m_bBrowserForwardEnabled) { bHandled = false; return S_OK; } }
bHandled = true;
// BOGUS assert - amcview calls Forward when ALT -> is pressed
// regardless of the state of the Forward button.
//ASSERT (m_iterCurrent != m_entries.end());
if(m_iterCurrent == m_entries.end()) return S_FALSE;
++m_iterCurrent;
if(m_iterCurrent == m_entries.end()) return S_FALSE;
HRESULT hr = ExecuteCurrent(); if(FAILED(hr)) return hr;
if(m_iterCurrent->IsWebEntry()) { if(m_bPageBreak) // if we're at a page break, go past it.
bHandled = false; // this tells the caller to use the Browser's Forward button.
}
return hr; }
/*+-------------------------------------------------------------------------*
* * CHistoryList::ExecuteCurrent * * PURPOSE: Sets the state of MMC to that of the current History entry. Called * by Back() and Forward(). * * RETURNS: * HRESULT * *+-------------------------------------------------------------------------*/ HRESULT CHistoryList::ExecuteCurrent() { DECLARE_SC(sc, TEXT("CHistoryList::ExecuteCurrent")); INodeCallback* pNC = m_pView->GetNodeCallback(); MTNODEID id;
TraceHistory(TEXT("CHistoryList::ExecuteCurrent"), m_iterCurrent);
pNC->GetMTNodeID (m_iterCurrent->hnode, &id); m_navState = MMC_HISTORY_NAVIGATING;
// store values to local variables to avoid losing them
// when an entry is removed from history
GUID guidTaskpad = m_iterCurrent->guidTaskpad; bool bIsListEntry = m_iterCurrent->IsListEntry(); DWORD viewMode = m_iterCurrent->viewMode;
m_pView->SelectNode (id, guidTaskpad);
if(bIsListEntry) { sc = m_pView->ScChangeViewMode(viewMode); if (sc) sc.TraceAndClear(); }
m_navState = MMC_HISTORY_READY;
MaintainWebBar(); return sc.ToHr(); }
void CHistoryList::MaintainWebBar() { bool bWebEntry = ((m_entries.size() != 0) && m_iterCurrent->IsWebEntry());
UpdateWebBar ( HB_BACK, ( bWebEntry && m_bBrowserBackEnabled ) || !IsFirst()); // back
UpdateWebBar ( HB_FORWARD, ( bWebEntry && m_bBrowserForwardEnabled ) || !IsLast () ); // forward
}
void CHistoryList::UpdateWebBar (HistoryButton button, BOOL bOn) { DECLARE_SC (sc, _T("CHistoryList::UpdateWebBar"));
if (NULL == m_pView) { sc = E_UNEXPECTED; return; }
CStandardToolbar* pStandardToolbar = m_pView->GetStdToolbar(); if (NULL == pStandardToolbar) { sc = E_UNEXPECTED; return; }
switch (button) { case HB_BACK: sc = pStandardToolbar->ScEnableButton(IDS_MMC_WEB_BACK, bOn); break; case HB_STOP: sc = pStandardToolbar->ScEnableButton(IDS_MMC_WEB_STOP, bOn); break; case HB_FORWARD: sc = pStandardToolbar->ScEnableButton(IDS_MMC_WEB_FORWARD, bOn); break; }
}
/*+-------------------------------------------------------------------------*
* * CHistoryList::ScGetCurrentResultViewType * * PURPOSE: Returns the current history entry. * * PARAMETERS: * CResultViewType & rvt : * int& viewMode : * GUID & guidTaskpad : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CHistoryList::ScGetCurrentResultViewType (CResultViewType &rvt, int& viewMode, GUID &guidTaskpad) { DECLARE_SC(sc, TEXT("CHistoryList::ScGetCurrentResultViewType"));
if(m_iterCurrent == m_entries.end()) return (sc = E_FAIL); // should never happen
rvt = m_iterCurrent->resultViewType; viewMode = m_iterCurrent->viewMode; guidTaskpad = m_iterCurrent->guidTaskpad;
return sc; }
void CHistoryList::SetCurrentViewMode (long nViewMode) { if(m_navState != MMC_HISTORY_READY) return; if(m_iterCurrent == m_entries.end()) return;
m_iterCurrent->viewMode = nViewMode; }
void CHistoryList::Clear() { Trace(tagHistory, TEXT("Clear")); m_entries.erase(m_entries.begin(), m_entries.end()); m_iterCurrent = m_entries.begin(); MaintainWebBar(); }
/*+-------------------------------------------------------------------------*
* * CHistoryList::ScModifyViewTab * * PURPOSE: Adds an entry to the history list, which is the same as the current * entry, except that the changes specified by the dwFlags and history * entry parameters are applied * * PARAMETERS: * const GUID& guidTab : Specifies guid of selected view tab * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CHistoryList::ScModifyViewTab(const GUID& guidTab) { DECLARE_SC(sc, TEXT("CHistoryList::ScAddModifiedEntry"));
// we are not going to modify anything if we navigating "Back" or ""Forward"
// thru the history enries
if ( m_bWithin_CHistoryList_Back || m_bWithin_CHistoryList_Forward ) return sc;
if( m_iterCurrent == m_entries.end() ) { return (sc = E_UNEXPECTED); }
// for web we cannot add new entries without reselecting the node
// (same is true about deleting subsequen entries)
// since that would make MMC and IE histories out of sync
// instead we just modify the current history entry
if ( !m_iterCurrent->IsWebEntry() ) // in case it is a regular entry
{ DeleteSubsequentEntries(); // delete everything ahead of this one.
// add an entry to the end of the list.
CHistoryEntry entry; ZeroMemory(&entry, sizeof(entry)); m_entries.push_back(entry); iterator iterNew = m_entries.end(); --iterNew; // point to the new entry.
// create a duplicate of the current entry.
*iterNew = *m_iterCurrent;
//set the pointer.
m_iterCurrent = iterNew; }
// change the guid of the tab.
m_iterCurrent->guidTaskpad = guidTab;
// we're done.
Compact(); MaintainWebBar(); return sc; }
/*+-------------------------------------------------------------------------*
* * CHistoryList::ScChangeViewMode * * PURPOSE: Changes the view mode of the current entry. Changing the view * mode does not add a new entry. Instead, history remembers the last * view mode that a node was at and always restores to that * * PARAMETERS: * int viewMode : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CHistoryList::ScChangeViewMode(int viewMode) { DECLARE_SC(sc, TEXT("CHistoryList::ScChangeViewMode"));
if( m_iterCurrent == m_entries.end() ) { return (sc = E_UNEXPECTED); }
m_iterCurrent->viewMode = viewMode; // set the view mode.
return sc; }
/*+-------------------------------------------------------------------------*
* * CHistoryList::DeleteEntry * * PURPOSE: Deletes all entries for a node from the history list. * * PARAMETERS: * HNODE hnode : * * RETURNS: * void * *+-------------------------------------------------------------------------*/ void CHistoryList::DeleteEntry (HNODE hnode) { for(iterator i= m_entries.begin(); i != m_entries.end(); ) { if(i->hnode == hnode) { iterator iNext = i; ++iNext; if(m_iterCurrent==i) m_iterCurrent=iNext; TraceHistory(TEXT("CHistoryList::Deleting entry"), i); m_entries.erase(i);
i= iNext; } else { ++i; } } Compact(); MaintainWebBar(); }
/*+-------------------------------------------------------------------------*
* * CHistoryList::Compact * * PURPOSE: 1) Removes redundancies in the history list by eliminating duplicates. * 2) Ensures that a maximum of MAX_HISTORY_ENTRIES entries is retained. * * RETURNS: * void * *+-------------------------------------------------------------------------*/ void CHistoryList::Compact() { if (m_entries.size() == 0) return;
// discard duplicates.
for (iterator i= m_entries.begin(); i != m_entries.end(); ) { iterator iNext = i; ++iNext; if(iNext == m_entries.end()) break;
// do not delete if it is a webentry (there is no way for us to tell IE
// to delete that history entry).
if ( (i->IsWebEntry() == false) && ( *i == *iNext)) { if(m_iterCurrent==i) m_iterCurrent=iNext;
TraceHistory(TEXT("CHistoryList::Deleting entry"), i); m_entries.erase(i); i = iNext; } else { ++i; } }
iterator iter = m_entries.begin(); iterator iterNext = iter; int nExcess = m_entries.size() - MAX_HISTORY_ENTRIES; while(nExcess-- > 0) { iterNext = iter; ++iterNext;
if(iter == m_iterCurrent) // make sure we don't delete the current entry.
break;
TraceHistory(TEXT("CHistoryList::Deleting entry"), i); m_entries.erase(iter); iter = iterNext; } }
/***************************************************************************\
* * METHOD: CHistoryList::PreviousWebPagesExist * * PURPOSE: looks back to see if there are any web pages in the history * up to the current mark (including it) * * PARAMETERS: * * RETURNS: * bool - result true = there is web pages * \***************************************************************************/ bool CHistoryList::PreviousWebPagesExist() { if ( m_entries.size() && m_iterCurrent == m_entries.end() ) { ASSERT(FALSE); // need to point to a valid entry !!!
return false; }
// will loop past the current entry.
iterator end = m_iterCurrent; ++end;
for ( iterator it = m_entries.begin(); it != end; ++it ) { if ( it->IsWebEntry() ) return true; }
return false; }
/*+-------------------------------------------------------------------------*
* ScGetPageBreakURL * * Returns the URL for MMC's HTML page containing a page break. *--------------------------------------------------------------------------*/
SC ScGetPageBreakURL(CStr& strPageBreakURL) { DECLARE_SC (sc, _T("GetPageBreakURL"));
/*
* clear out the old value, if any */ strPageBreakURL.Empty();
// generate new pagebreak URL every time ( prevent web browser from compacting it)
static int nPageBreak = 0; strPageBreakURL.Format( _T("%s%d"), PAGEBREAK_URL, ++nPageBreak );
return (sc); }
|