///////////////////////////////////////////////////////////////
//
// BrowserHelperObject.cpp
//
// Created: 01/04/2004
// Copyright (c) 2004 Ralph Hare (ralph.hare@ysgyfarnog.co.uk)
// All rights reserved.
//
// The code and information is pro6vided "as-is" without
// warranty of any kind, either expressed or implied.
//
///////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "BrowserHelperObject.h"
#include "ExplorerListDlg.h"
#include "ComUtils.h"
#include "UserActionManager.h"
#include "TrailManager.h"
namespace
{
/**
* Data to pass to send message to scroll to the top or bottom
* of the browser window. Values retrieved by pressing control-home
* and control-end in web browser while Spy was attached
**/
WPARAM GOTO_TOP_CODE = 0x24;
WPARAM GOTO_BOTTOM_CODE = 0x23;
LPARAM KEYDOWN_DATA = 0x01470001;
LPARAM KEYUP_DATA = 0xC1470001;
/**
* Create a new web browser window
* @param address the address of the initial page for the web browser -
* may be NULL, in which case the new window is opened at the
* users home page
* @param isForeground if true the web browser is brought to the front
* of the z-order. if false the web browser is sent to the back
* of the z-order
**/
void CreateBrowser( const CComBSTR &address, bool isForeground = true )
{
ThrowHResult hr;
try
{
//
// try and create a new web browser application
//
CComPtr< IWebBrowserApp > pApp;
hr = pApp.CoCreateInstance( __uuidof( InternetExplorer ) );
//
// if we can get the web browser interface, then
// we can navigate to our passed address
//
CComPtr< IWebBrowser2 > pWB2;
hr = pApp->QueryInterface(
IID_IWebBrowser2,
reinterpret_cast< void ** >( &pWB2 )
);
if( pWB2 )
{
//
// if the user has specified a page to navigate to
// then open that page, otherwise goto the configured
// home page
//
if( address.Length() )
{
//
// need a dummy parameter to pass to Navigate2
//
CComVariant dummy;
CComVariant url( address.m_str );
hr = pWB2->Navigate2( &url, &dummy, &dummy, &dummy, &dummy );
}
else
{
hr = pWB2->GoHome();
}
}
HWND hWndFocus = ::GetFocus();
HWND hWndFore = ::GetForegroundWindow();
hr = pApp->put_Visible( VARIANT_TRUE );
HWND hWnd;
hr = pApp->get_HWND( reinterpret_cast< long * >( &hWnd ) );
if( isForeground )
{
::SetFocus( hWnd );
::SetForegroundWindow( hWnd );
}
else
{
::SetFocus( hWndFocus );
::SetForegroundWindow( hWndFore );
}
}
catch( HRESULT )
{
}
}
/**
* If the passed element is an anchor or area element, extract
* its HREF attribute. It its not, recursively ascend
* up the element hierarchy until we reach an anchor element
* or the top of the document.
* (Note: Area elements appear within image maps)
* @param pElement the element to extract the HREF attribute from
* @return the contents of the HREF attribute of the element (or
* an element its hierarchy), or a NULL BSTR if no link can be
* found
**/
CComBSTR GetHRefFromAnchor( IHTMLElement *pElement )
{
ThrowHResult hr;
try
{
if( pElement )
{
CComQIPtr< IHTMLAnchorElement > pAnchor = pElement;
if( pAnchor )
{
CComBSTR url;
hr = pAnchor->get_href( &url );
return url;
}
CComQIPtr< IHTMLAreaElement > pArea = pElement;
if( pArea )
{
CComBSTR url;
hr = pArea->get_href( &url );
return url;
}
CComPtr< IHTMLElement > pParent;
hr = pElement->get_parentElement( &pParent );
return GetHRefFromAnchor( pParent );
}
}
catch( HRESULT )
{
}
return CComBSTR();
}
/**
* Is there an anchor element in the web browsers document
* that begins with a particular tag. Note that the HTML
* for an anchor is of the form:
*
* <code>
* <A HREF="link.html">this is the tag</A>
* </code>
*
* This function does a case insensitive compare of upto the first N
* characters (where N is the length of the tag_ argument) of the inner text
* ('this is the tag') of the element.
**/
IHTMLElement * FindAnchor( IWebBrowser2 *pWB, LPCWSTR tag_ )
{
if( tag_ == NULL )
{
return NULL;
}
std::wstring tag( tag_ );
wcslwr( &tag[ 0 ] );
ThrowHResult hr;
try
{
CComPtr< IDispatch > pDisp;
hr = pWB->get_Document( &pDisp );
CComQIPtr< IHTMLDocument2 > pDoc = pDisp;
if( pDoc == NULL )
{
return NULL;
}
CComPtr< IHTMLElementCollection > pLinks;
hr = pDoc->get_links( &pLinks );
long cntLinks = 0;
hr = pLinks->get_length( &cntLinks );
for( long idx = 0; idx < cntLinks; ++idx )
{
pDisp = NULL;
hr = pLinks->item( CComVariant( idx ), CComVariant(), &pDisp );
CComQIPtr< IHTMLElement > pElement = pDisp;
if( pElement )
{
CComBSTR txt;
hr = pElement->get_innerText( &txt );
if( txt.Length() )
{
if( wcsncmp(
wcslwr( txt.m_str ),
tag.c_str(),
tag.size()
) == 0 )
{
pElement.p->AddRef();
return pElement;
}
}
}
}
}
catch( HRESULT )
{
}
return NULL;
}
/**
* Returns the selected text within the active document
**/
CComBSTR GetSelection( IWebBrowser2 *pWB )
{
if( pWB == NULL )
{
return CComBSTR();
}
ThrowHResult hr;
try
{
CComPtr< IDispatch > pDisp;
hr = pWB->get_Document( &pDisp );
CComQIPtr< IHTMLDocument2 > pDoc = pDisp;
if( pDoc == NULL )
{
return CComBSTR();
}
CComPtr< IHTMLSelectionObject > pSelection;
hr = pDoc->get_selection( &pSelection );
if( pSelection == NULL )
{
return CComBSTR();
}
pDisp = NULL;
hr = pSelection->createRange( &pDisp );
CComQIPtr< IHTMLTxtRange > pRange = pDisp;
if( pRange )
{
CComBSTR txt;
hr = pRange->get_text( &txt );
return txt;
}
}
catch( HRESULT )
{
}
return CComBSTR();
}
/**
* RAII class to simulate a key press. The constructor
* simulates the key down event, the destructor the key up
* event
* @see keybd_event
**/
class KeyboardEvent
{
public:
KeyboardEvent( BYTE bVk, BYTE bScan ) :
m_bVk( bVk ),
m_bScan( bScan )
{
keybd_event( bVk, bScan, 0, 0 );
}
~KeyboardEvent()
{
keybd_event( m_bVk, m_bScan, KEYEVENTF_KEYUP, 0 );
}
// not implemented
private:
KeyboardEvent( const KeyboardEvent & );
KeyboardEvent & operator = ( const KeyboardEvent & );
private:
const BYTE m_bVk;
const BYTE m_bScan;
};
}
BrowserHelperObject::BrowserHelperObject() :
m_pWebBrowser(),
m_pBrowserWatcher()
{
}
void BrowserHelperObject::FinalRelease()
{
}
STDMETHODIMP BrowserHelperObject::SetSite( IUnknown *pUnkSite )
{
if( pUnkSite == NULL )
{
return E_POINTER;
}
ThrowHResult hr;
try
{
m_pWebBrowser = pUnkSite; // implicit QI
if( m_pWebBrowser == NULL )
{
hr = E_NOINTERFACE;
}
m_pBrowserWatcher = AutoBrowserWatcher(
BrowserWatcher::CreateInstance(
m_pWebBrowser,
this
)
);
//
// initialise the trail manager now, so its ready for use
// the first time the user performs a gesture
//
TrailManager::GetInstance().GetTrail();
}
catch( HRESULT )
{
}
return hr;
}
void BrowserHelperObject::GotoPrevious( const ActionPacket & )
{
if( FAILED( m_pWebBrowser->GoBack() ) )
{
CComPtr< IHTMLElement > pAnchor = FindAnchor( m_pWebBrowser, L"Previous" );
if( pAnchor )
{
CComVariant dummy;
CComVariant href( GetHRefFromAnchor( pAnchor ) );
m_pWebBrowser->Navigate2( &href, &dummy, &dummy, &dummy, &dummy );
}
}
}
void BrowserHelperObject::GotoNext( const ActionPacket & )
{
if( FAILED( m_pWebBrowser->GoForward() ) )
{
CComPtr< IHTMLElement > pAnchor = FindAnchor( m_pWebBrowser, L"Next" );
if( pAnchor )
{
CComVariant dummy;
CComVariant href( GetHRefFromAnchor( pAnchor ) );
m_pWebBrowser->Navigate2( &href, &dummy, &dummy, &dummy, &dummy );
}
}
}
void BrowserHelperObject::GotoTop( const ActionPacket &ap )
{
::SendMessage( ap.hWnd, WM_KEYDOWN, GOTO_TOP_CODE, KEYDOWN_DATA );
::SendMessage( ap.hWnd, WM_KEYUP, GOTO_TOP_CODE, KEYUP_DATA );
}
void BrowserHelperObject::GotoBottom( const ActionPacket &ap )
{
::SendMessage( ap.hWnd, WM_KEYDOWN, GOTO_BOTTOM_CODE, KEYDOWN_DATA );
::SendMessage( ap.hWnd, WM_KEYUP, GOTO_BOTTOM_CODE, KEYUP_DATA );
}
void BrowserHelperObject::GoHome( const ActionPacket & )
{
m_pWebBrowser->GoHome();
}
void BrowserHelperObject::Close( const ActionPacket & )
{
CComQIPtr< IWebBrowserApp > pApp = m_pWebBrowser;
if( pApp )
{
pApp->Quit();
}
}
void BrowserHelperObject::OpenNewForeWindow( const ActionPacket &ap )
{
OpenNewWindow( ap.hWnd, ap.pt, true );
}
void BrowserHelperObject::OpenNewBackWindow( const ActionPacket &ap )
{
OpenNewWindow( ap.hWnd, ap.pt, false );
}
void BrowserHelperObject::Refresh( const ActionPacket & )
{
m_pWebBrowser->Refresh();
}
void BrowserHelperObject::Reload( const ActionPacket & )
{
CComVariant var( REFRESH_COMPLETELY );
m_pWebBrowser->Refresh2( &var );
}
void BrowserHelperObject::Minimise( const ActionPacket &ap )
{
::ShowWindow( ::GetAncestor( ap.hWnd, GA_ROOT ), SW_MINIMIZE );
}
void BrowserHelperObject::Restore( const ActionPacket &ap )
{
::ShowWindow( ::GetAncestor( ap.hWnd, GA_ROOT ), SW_RESTORE );
}
void BrowserHelperObject::Duplicate( const ActionPacket & )
{
ThrowHResult hr;
try
{
CComPtr< IDispatch > pDisp;
hr = m_pWebBrowser->get_Document( &pDisp );
CComQIPtr< IHTMLDocument2 > pDoc = pDisp;
if( pDoc )
{
CComBSTR url;
hr = pDoc->get_URL( &url );
CreateBrowser( url );
}
}
catch( HRESULT )
{
}
}
void BrowserHelperObject::ScrollWindows( const ActionPacket &ap )
{
ExplorerListDlg::Invoke( ::GetAncestor( ap.hWnd, GA_ROOT ) );
}
void BrowserHelperObject::Maximise( const ActionPacket &ap )
{
::ShowWindow( ::GetAncestor( ap.hWnd, GA_ROOT ), SW_MAXIMIZE );
}
void BrowserHelperObject::CommandOpen( const ActionPacket & )
{
ExecuteCommand( OLECMDID_OPEN );
}
void BrowserHelperObject::CommandSave( const ActionPacket & )
{
ExecuteCommand( OLECMDID_SAVE );
}
void BrowserHelperObject::CommandSaveAs( const ActionPacket & )
{
ExecuteCommand( OLECMDID_SAVEAS );
}
void BrowserHelperObject::CommandPrint( const ActionPacket & )
{
ExecuteCommand( OLECMDID_PRINT );
}
void BrowserHelperObject::CommandPrintPreview( const ActionPacket & )
{
ExecuteCommand( OLECMDID_PRINTPREVIEW );
}
void BrowserHelperObject::CommandPageSetup( const ActionPacket & )
{
ExecuteCommand( OLECMDID_PAGESETUP );
}
void BrowserHelperObject::CommandProperties( const ActionPacket & )
{
ExecuteCommand( OLECMDID_PROPERTIES );
}
void BrowserHelperObject::CommandCut( const ActionPacket & )
{
ExecuteCommand( OLECMDID_CUT );
}
void BrowserHelperObject::CommandCopy( const ActionPacket & )
{
ExecuteCommand( OLECMDID_COPY );
}
void BrowserHelperObject::CommandPaste( const ActionPacket & )
{
ExecuteCommand( OLECMDID_PASTE );
}
void BrowserHelperObject::CommandUndo( const ActionPacket & )
{
ExecuteCommand( OLECMDID_UNDO );
}
void BrowserHelperObject::CommandRedo( const ActionPacket & )
{
ExecuteCommand( OLECMDID_REDO );
}
void BrowserHelperObject::CommandSelectAll( const ActionPacket & )
{
ExecuteCommand( OLECMDID_SELECTALL );
}
void BrowserHelperObject::CommandClearSelection( const ActionPacket & )
{
ExecuteCommand( OLECMDID_CLEARSELECTION );
}
void BrowserHelperObject::CommandStop( const ActionPacket & )
{
ExecuteCommand( OLECMDID_STOP );
}
void BrowserHelperObject::User0( const ActionPacket &ap )
{
UserAction( ap.hWnd, 0 );
}
void BrowserHelperObject::User1( const ActionPacket &ap )
{
UserAction( ap.hWnd, 1 );
}
void BrowserHelperObject::User2( const ActionPacket &ap )
{
UserAction( ap.hWnd, 2 );
}
void BrowserHelperObject::User3( const ActionPacket &ap )
{
UserAction( ap.hWnd, 3 );
}
void BrowserHelperObject::User4( const ActionPacket &ap )
{
UserAction( ap.hWnd, 4 );
}
void BrowserHelperObject::User5( const ActionPacket &ap )
{
UserAction( ap.hWnd, 5 );
}
void BrowserHelperObject::User6( const ActionPacket &ap )
{
UserAction( ap.hWnd, 6 );
}
void BrowserHelperObject::User7( const ActionPacket &ap )
{
UserAction( ap.hWnd, 7 );
}
void BrowserHelperObject::User8( const ActionPacket &ap )
{
UserAction( ap.hWnd, 8 );
}
void BrowserHelperObject::User9( const ActionPacket &ap )
{
UserAction( ap.hWnd, 9 );
}
bool BrowserHelperObject::OnGesture(
HWND hWnd,
GestureManager::Pattern pattern,
const Path &path
)
{
return Execute( hWnd, pattern, path );
}
bool BrowserHelperObject::Execute( HWND hWnd, int pattern, const Path &path )
{
ACTION action = GestureManager::GetAction( pattern );
if( action )
{
( this->*action )( ActionPacket( hWnd, path.front() ) );
return true;
}
return false;
}
void BrowserHelperObject::ExecuteCommand( OLECMDID cmdId )
{
if( m_pWebBrowser )
{
m_pWebBrowser->ExecWB( cmdId, OLECMDEXECOPT_DODEFAULT, NULL, NULL );
}
}
void BrowserHelperObject::UserAction( HWND hWnd, size_t userAction )
{
/**
* The AutoKeyboardEvent class optionally creates a KeyboardEvent object
**/
struct AutoKeyboardEvent
{
AutoKeyboardEvent( bool doIt, BYTE virtKey ) :
m_event( doIt ? new KeyboardEvent( virtKey, static_cast< BYTE >( ::MapVirtualKey( virtKey, 0 ) | 0x80 ) ) : NULL )
{
}
private:
std::auto_ptr< KeyboardEvent > m_event;
};
DWORD shortCut = UserActionManager::GetInstance().GetShortcutKey( userAction );
if( shortCut )
{
WORD modifier = HIWORD( shortCut );
//
// use the AutoKeyboardEvent to ensure our key-up and key-down
// events are triggered in the correct sequence
//
AutoKeyboardEvent ctrlKey( ( modifier & HOTKEYF_CONTROL ) == HOTKEYF_CONTROL, VK_CONTROL );
AutoKeyboardEvent altKey( ( modifier & HOTKEYF_ALT ) == HOTKEYF_CONTROL, VK_MENU );
AutoKeyboardEvent shiftKey( ( modifier & HOTKEYF_SHIFT ) == HOTKEYF_SHIFT, VK_SHIFT);
AutoKeyboardEvent virtKey( true, static_cast< BYTE >( LOWORD( shortCut ) ) );
}
}
void BrowserHelperObject::OpenNewWindow( HWND hWnd, const POINT &pt, bool isForeground )
{
ThrowHResult hr;
try
{
//
// find the html element under the mouse at the start
// of the gesture
//
CComPtr< IHTMLElement > pElement;
hr = m_pBrowserWatcher->HitTest( hWnd, pt, &pElement );
//
// does it, or one of its children, have an href attribute?
//
CComBSTR href = GetHRefFromAnchor( pElement );
//
// if not, has the user performed the gesture over selected text?
//
if( href.Length() == 0 )
{
href = GetSelection( m_pWebBrowser );
}
CreateBrowser( href, isForeground );
}
catch( HRESULT )
{
}
}