1077 lines
31 KiB
C++
1077 lines
31 KiB
C++
/* sample source code for IE4 view extension
|
|
|
|
* Copyright Microsoft 1996
|
|
|
|
* This file implements the IShellView2 interface
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
#include "wininet.h"
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::GetWindow ( HWND * lphwnd)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
if ( m_hWnd != NULL )
|
|
{
|
|
*lphwnd = m_hWnd;
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::ContextSensitiveHelp ( BOOL fEnterMode)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//*** IsVK_TABCycler -- is key a TAB-equivalent
|
|
// ENTRY/EXIT
|
|
// dir 0 if not a TAB, non-0 if a TAB
|
|
// NOTES
|
|
// NYI: -1 for shift+tab, 1 for tab
|
|
|
|
int IsVK_TABCycler(MSG *pMsg)
|
|
{
|
|
int nDir = 0;
|
|
|
|
if (!pMsg)
|
|
return nDir;
|
|
|
|
if (pMsg->message != WM_KEYDOWN)
|
|
return nDir;
|
|
if (! (pMsg->wParam == VK_TAB || pMsg->wParam == VK_F6))
|
|
return nDir;
|
|
|
|
nDir = (GetKeyState(VK_SHIFT) < 0) ? -1 : 1;
|
|
|
|
return nDir;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::TranslateAccelerator ( LPMSG lpmsg)
|
|
{
|
|
HRESULT hr = S_FALSE; // Default has it that message isn't processed.
|
|
|
|
if ( m_fTranslateAccel )
|
|
{
|
|
// in label edit mode, disabled explorers accelerator
|
|
// process this msg so the exploer does not get to translate
|
|
TranslateMessage(lpmsg);
|
|
DispatchMessageWrapW(lpmsg);
|
|
hr = S_OK;
|
|
}
|
|
|
|
else if ( m_hAccel )
|
|
{
|
|
// this table needs to be reduced for the new view as the def-view should pick up
|
|
// most keyboard accelerators...
|
|
if ( ::TranslateAcceleratorWrapW( m_hWnd, m_hAccel, lpmsg ) )
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else if (IsVK_TABCycler(lpmsg))
|
|
{
|
|
SHELLSTATE ss;
|
|
|
|
// If it's a <tab> with WebView off and we don't have focus then take focus.
|
|
// Trident handles the WebView on focus case.
|
|
|
|
SHGetSetSettings(&ss, SSF_WEBVIEW, FALSE);
|
|
if (!ss.fWebView && (GetFocus() != m_hWndListView))
|
|
{
|
|
SetFocus(m_hWndListView);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::EnableModeless ( BOOL fEnable)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::UIActivate ( UINT uState)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::Refresh ()
|
|
{
|
|
// empty the view
|
|
Clear( m_hWndListView );
|
|
|
|
CheckViewOptions();
|
|
|
|
// remove all tasks currently in the queue ..
|
|
m_pScheduler->Status( ITSSFLAG_KILL_ON_DESTROY, ITSS_THREAD_TIMEOUT_NO_CHANGE );
|
|
|
|
m_pScheduler->RemoveTasks( TOID_NULL, ITSAT_DEFAULT_LPARAM, TRUE );
|
|
|
|
// clear out the thumbnail cache...
|
|
Assert( m_pImageCache );
|
|
m_pImageCache->Flush( FALSE );
|
|
|
|
// this needs moving on the background somehow....
|
|
EnumFolder( );
|
|
|
|
SortBy( m_iSortBy, TRUE );
|
|
FocusOnSomething();
|
|
UpdateStatusBar();
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::CreateViewWindow ( IShellView *lpPrevView,
|
|
LPCFOLDERSETTINGS lpfs,
|
|
IShellBrowser * psb,
|
|
RECT * prcView,
|
|
HWND *phWnd )
|
|
{
|
|
// delegate the call to the new IShellView2 method ..
|
|
|
|
SV2CVW2_PARAMS cParams;
|
|
|
|
cParams.cbSize = sizeof(SV2CVW2_PARAMS);
|
|
cParams.psvPrev = lpPrevView;
|
|
cParams.pfs = lpfs;
|
|
cParams.psbOwner = psb;
|
|
cParams.prcView = prcView;
|
|
cParams.pvid = NULL;
|
|
|
|
HRESULT hr = CreateViewWindow2( &cParams );
|
|
|
|
*phWnd = cParams.hwndView;
|
|
|
|
return (hr);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::DestroyViewWindow ()
|
|
{
|
|
// flag we are destroying.....
|
|
m_fDestroying = TRUE;
|
|
|
|
if ( m_pScheduler )
|
|
{
|
|
m_pScheduler->Status( ITSSFLAG_KILL_ON_DESTROY, ITSS_THREAD_TIMEOUT_NO_CHANGE );
|
|
m_pScheduler->Release();
|
|
m_pScheduler = NULL;
|
|
}
|
|
|
|
if ( m_pDiskCache )
|
|
{
|
|
// at this point we assume that we have no lock,
|
|
m_pDiskCache->Close( NULL );
|
|
m_pDiskCache->Release();
|
|
m_pDiskCache = NULL;
|
|
}
|
|
|
|
// release ourselves from the drag drop ..
|
|
RevokeDragDrop( m_hWndListView );
|
|
|
|
Assert( m_pDropTarget );
|
|
m_pDropTarget->Release();
|
|
m_pDropTarget = NULL;
|
|
|
|
if (m_pDragImages)
|
|
{
|
|
m_pDragImages->Release();
|
|
m_pDragImages = NULL;
|
|
}
|
|
|
|
// free the global lock on this object !!
|
|
IUnknown *pUnk = NULL;
|
|
HRESULT hr = ((IShellView2 *)this)->QueryInterface( IID_IUnknown, (void **) & pUnk );
|
|
Assert( SUCCEEDED( hr ));
|
|
|
|
CoLockObjectExternal( pUnk, FALSE, FALSE );
|
|
pUnk->Release();
|
|
|
|
// NULL out the HWNDs, so that if we are accidentally in label edit mode, we know to fail quick
|
|
// and gracefully...
|
|
HWND hwndTemp = m_hWnd;
|
|
HWND hwndListViewTemp = m_hWndListView;
|
|
m_hWnd = NULL;
|
|
m_hWndListView = NULL;
|
|
|
|
Clear( hwndListViewTemp );
|
|
|
|
SetAutomationObject(NULL);
|
|
|
|
// destroy the window, this should inturn destroy the Listview
|
|
DestroyWindow( hwndTemp );
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::GetCurrentInfo ( LPFOLDERSETTINGS lpfs)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::AddPropertySheetPages ( DWORD dwReserved,
|
|
LPFNADDPROPSHEETPAGE lpfn,
|
|
LPARAM lparam)
|
|
{
|
|
/*[TODO: need to add one here ]*/
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::SaveViewState ()
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::SelectItem ( LPCITEMIDLIST pidlItem,
|
|
UINT uFlags)
|
|
{
|
|
if (!pidlItem)
|
|
{
|
|
if (uFlags != SVSI_DESELECTOTHERS)
|
|
{
|
|
// I only know how to deselect everything
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
ListView_SetItemState(m_hWndListView, -1, 0, LVIS_SELECTED);
|
|
return(S_OK);
|
|
}
|
|
|
|
int i = FindItem(pidlItem);
|
|
if (i != -1)
|
|
{
|
|
// The SVSI_EDIT flag also contains SVSI_SELECT and as such
|
|
// a simple & wont work!
|
|
if ((uFlags & SVSI_EDIT) == SVSI_EDIT)
|
|
{
|
|
// Grab focus if the listview (or any of it's children) don't already have focus
|
|
HWND hwndFocus = GetFocus();
|
|
if (!hwndFocus ||
|
|
!(hwndFocus == m_hWndListView || IsChild(m_hWndListView, hwndFocus)))
|
|
{
|
|
SetFocus(m_hWnd);
|
|
}
|
|
ListView_EditLabel(m_hWndListView, i);
|
|
}
|
|
else
|
|
{
|
|
UINT stateMask = LVIS_SELECTED;
|
|
UINT state = (uFlags & SVSI_SELECT) ? LVIS_SELECTED : 0;
|
|
if (uFlags & SVSI_FOCUSED)
|
|
{
|
|
state |= LVIS_FOCUSED;
|
|
stateMask |= LVIS_FOCUSED;
|
|
}
|
|
|
|
// See if we should first deselect everything else
|
|
if (uFlags & SVSI_DESELECTOTHERS)
|
|
ListView_SetItemState(m_hWndListView, -1, 0, LVIS_SELECTED);
|
|
|
|
ListView_SetItemState(m_hWndListView, i, state, stateMask);
|
|
|
|
if (uFlags & SVSI_ENSUREVISIBLE)
|
|
ListView_EnsureVisible(m_hWndListView, i, FALSE);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return (E_FAIL);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::GetItemObject ( UINT uItem,
|
|
REFIID riid,
|
|
void **ppv)
|
|
{
|
|
if ( ppv == NULL )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
*ppv = NULL;
|
|
|
|
switch( uItem )
|
|
{
|
|
case SVGIO_BACKGROUND:
|
|
{
|
|
if ( riid == IID_IContextMenu )
|
|
{
|
|
return this->CreateBackgroundMenu( (LPCONTEXTMENU *) ppv );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SVGIO_SELECTION:
|
|
{
|
|
// use the context menu wrapper so that we get the capture item on the
|
|
// file menu as well....
|
|
int iCount = ListView_GetSelectedCount( m_hWndListView );
|
|
if ( iCount == 0 )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
LPCITEMIDLIST * apidl = new LPCITEMIDLIST[iCount];
|
|
GetSelectionPidlList( m_hWndListView, iCount, apidl, -1 );
|
|
UINT uiFlags = 0;
|
|
HRESULT hr = NOERROR;
|
|
|
|
if ( riid == IID_IContextMenu )
|
|
{
|
|
CComObject<CThumbnailMenu> * pMenuTmp = new CComObject<CThumbnailMenu>;
|
|
if ( pMenuTmp == NULL )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
hr = pMenuTmp->QueryInterface( riid, ppv );
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
hr = pMenuTmp->Init( this, & uiFlags, apidl, iCount );
|
|
}
|
|
|
|
if ( FAILED( hr ))
|
|
{
|
|
delete pMenuTmp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = m_pFolder->GetUIObjectOf( m_hWnd,
|
|
iCount,
|
|
apidl,
|
|
riid,
|
|
&uiFlags,
|
|
ppv );
|
|
}
|
|
|
|
delete [] apidl;
|
|
return hr;
|
|
}
|
|
break;
|
|
|
|
case SVGIO_ALLVIEW:
|
|
/*[TODO: this needs doing if there is a thread for background extraction ]*/
|
|
break;
|
|
}
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::GetView ( SHELLVIEWID* pvid, ULONG uView)
|
|
{
|
|
if ( pvid == NULL )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// as a view extension, if we get called, just return the VID for
|
|
// our view....
|
|
*pvid = VID_Thumbnails;
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::CreateViewWindow2 ( LPSV2CVW2_PARAMS lpParams)
|
|
{
|
|
if ( m_pFolderCB == NULL )
|
|
{
|
|
// just incase so we don't die horribly......
|
|
return E_FAIL;
|
|
}
|
|
|
|
Assert( lpParams != NULL );
|
|
if ( lpParams == NULL )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
Assert( lpParams->psbOwner != NULL );
|
|
Assert( lpParams->psvPrev != NULL );
|
|
|
|
// do some funky stuff to get the arrange parameter from the defview
|
|
DWORD dwSortBy = DETAILSCOL_DEFAULT;
|
|
IShellFolderView *pSFV;
|
|
HRESULT hr = lpParams->psvPrev->QueryInterface( IID_IShellFolderView, ( void ** )&pSFV );
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
LPARAM lParam;
|
|
pSFV->GetArrangeParam( &lParam );
|
|
dwSortBy = ( DWORD ) lParam;
|
|
pSFV->Release( );
|
|
}
|
|
|
|
if ( lpParams->pvid != NULL )
|
|
{
|
|
// are we are being asked to create a view which isn't ours ?
|
|
if ( *(lpParams->pvid) != VID_Thumbnails )
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// get the parent window from the IShellView of the def-view
|
|
m_pDefView = lpParams->psvPrev;
|
|
m_pDefView->AddRef();
|
|
|
|
hr = m_pDefView->GetWindow( &m_hWndParent );
|
|
Assert( SUCCEEDED( hr ));
|
|
Assert( m_hWndParent != NULL );
|
|
|
|
// cache the setting so we can use them on our child window...
|
|
m_rgSettings = *(lpParams->pfs);
|
|
|
|
RECT * prcRect = lpParams->prcView;
|
|
|
|
// create the image cache (before we do the CreateWindow)....
|
|
hr = CoCreateInstance( CLSID_ImageListCache, NULL, CLSCTX_INPROC, IID_IImageCache2, (void **) &m_pImageCache );
|
|
if ( FAILED( hr ))
|
|
{
|
|
return hr;
|
|
}
|
|
UINT uBytesPerPix;
|
|
IMAGECACHEINITINFO rgInfo;
|
|
rgInfo.cbSize = sizeof( rgInfo );
|
|
rgInfo.dwMask = ICIIFLAG_LARGE;
|
|
rgInfo.rgSizeLarge.cx = m_iXSizeThumbnail;
|
|
rgInfo.rgSizeLarge.cy = m_iYSizeThumbnail;
|
|
rgInfo.iStart = 0;
|
|
rgInfo.iGrow = 5;
|
|
m_dwRecClrDepth = rgInfo.dwFlags = GetCurrentColorFlags( &uBytesPerPix );
|
|
|
|
hr = m_pImageCache->GetImageList( &rgInfo );
|
|
if ( FAILED( hr ))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
m_himlThumbs = rgInfo.himlLarge;
|
|
Assert( m_himlThumbs );
|
|
|
|
// GetImageList() will return S_FALSE if it was already created...
|
|
if ( m_dwRecClrDepth <= 8 )
|
|
{
|
|
// init the color table so that it matches The "special halftone palette"
|
|
m_hpal = SHCreateShellPalette( NULL );
|
|
PALETTEENTRY rgColours[256];
|
|
RGBQUAD rgDIBColours[256];
|
|
|
|
Assert( m_hpal );
|
|
int nColours = GetPaletteEntries(m_hpal, 0, ARRAYSIZE(rgColours), rgColours );
|
|
|
|
// SHGetShellPalette should always return a 256 colour palette
|
|
Assert( nColours == ARRAYSIZE( rgColours ));
|
|
|
|
// translate from the LOGPALETTE structure to the RGBQUAD structure ...
|
|
for ( int iColour = 0; iColour < nColours; iColour ++ )
|
|
{
|
|
rgDIBColours[iColour].rgbRed = rgColours[iColour].peRed;
|
|
rgDIBColours[iColour].rgbBlue = rgColours[iColour].peBlue;
|
|
rgDIBColours[iColour].rgbGreen = rgColours[iColour].peGreen;
|
|
rgDIBColours[iColour].rgbReserved = 0;
|
|
}
|
|
|
|
ImageList_SetColorTable( m_himlThumbs, 0, nColours, rgDIBColours );
|
|
}
|
|
|
|
m_iMaxCacheSize = CalcCacheMaxSize( &rgInfo.rgSizeLarge, uBytesPerPix );
|
|
|
|
// create everything because we are a new window .....
|
|
DWORD dwStyle = WS_CHILD | WS_TABSTOP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
|
|
if (!(m_rgSettings.fFlags & FWF_NOVISIBLE))
|
|
dwStyle |= WS_VISIBLE;
|
|
|
|
m_hWnd = CreateWindowExWrapW( 0, VIEWCLASSNAME, L"",
|
|
dwStyle,
|
|
prcRect->left, prcRect->top,
|
|
prcRect->right - prcRect->left,
|
|
prcRect->bottom - prcRect->top,
|
|
m_hWndParent, NULL, g_hinstDll, this );
|
|
|
|
if ( m_hWnd == NULL )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
UpdateWindow( m_hWnd );
|
|
|
|
// hold a ref to the browser interface
|
|
lpParams->psbOwner->AddRef();
|
|
m_pBrowser = lpParams->psbOwner;
|
|
|
|
// check to see if we are in the common dialog, if so, get the interface ...
|
|
// ignore the return result, the pointer will still be null if it
|
|
// fails
|
|
m_pBrowser->QueryInterface( IID_ICommDlgBrowser, (void **) & m_pCommDlg );
|
|
|
|
HWND hWndTree = NULL;
|
|
|
|
// check to see if we are in explore mode ..
|
|
hr = m_pBrowser->GetControlWindow( FCW_TREE, & hWndTree );
|
|
|
|
m_fExploreMode = ( hr == NOERROR && hWndTree != NULL );
|
|
|
|
// NOTE: the DefView owns the Toolbar and the menus, so we do not alter these
|
|
// NOTE: in a view extension
|
|
|
|
// ask the folder for the pidl...
|
|
IPersistFolder2 * ppf2;
|
|
hr = m_pFolder->QueryInterface( IID_IPersistFolder2, (void **) & ppf2 );
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ppf2->GetCurFolder(&m_pidl);
|
|
ppf2->Release();
|
|
}
|
|
else
|
|
{
|
|
// ask the callback for the pidl ...
|
|
hr = m_pFolderCB->MessageSFVCB( SFVM_THISIDLIST, NULL, (LPARAM) &m_pidl );
|
|
if ( FAILED( hr ))
|
|
{
|
|
SHChangeNotifyEntry rgEntry;
|
|
UINT uFlags = 0;
|
|
m_ulShellRegId = 0;
|
|
|
|
// ask the callback about notifications ...
|
|
// this is another way of getting the pidl....
|
|
hr = m_pFolderCB->MessageSFVCB( SFVM_GETNOTIFY, (WPARAM) &( rgEntry.pidl ), (LPARAM) &uFlags );
|
|
if ( FAILED( hr ) || !rgEntry.pidl )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
// cache the pidl...
|
|
m_pidl = ILClone( rgEntry.pidl );
|
|
}
|
|
}
|
|
|
|
hr = CoCreateInstance( CLSID_ShellThumbnailDiskCache, NULL, CLSCTX_INPROC, IID_IShellImageStore, (void **) & m_pDiskCache );
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
IPersistFolder * ppf;
|
|
hr = m_pDiskCache->QueryInterface( IID_IPersistFolder, (void **) & ppf );
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ppf->Initialize( m_pidl );
|
|
ppf->Release();
|
|
}
|
|
|
|
if ( FAILED( hr ))
|
|
{
|
|
// can't get the disk cache, live without it...
|
|
m_pDiskCache->Release();
|
|
m_pDiskCache = NULL;
|
|
}
|
|
}
|
|
|
|
// do the enum after we have registered the cache's full path
|
|
EnumFolder();
|
|
SortBy( dwSortBy );
|
|
m_iSortBy = (int) dwSortBy;
|
|
|
|
IDropTarget *pDrop = NULL; // create a tearoff DropTarget
|
|
|
|
hr = NOERROR;
|
|
m_pDropTarget = new CComObject<CViewDropTarget>;
|
|
if ( m_pDropTarget!= NULL )
|
|
{
|
|
hr = m_pDropTarget->Init(( CDropTargetClient *) this, m_pFolder, m_hWnd );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
delete m_pDropTarget;
|
|
m_pDropTarget = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = m_pDropTarget->QueryInterface( IID_IDropTarget, (void **) & pDrop );
|
|
Assert( SUCCEEDED( hr ));
|
|
|
|
// addref our pointer because the objects are created with a zero ref count
|
|
m_pDropTarget->AddRef();
|
|
}
|
|
}
|
|
|
|
// This is allowed to fail in Non-NT5 cases.
|
|
CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER, IID_IDragSourceHelper, (void**)&m_pDragImages);
|
|
|
|
// if the above fails, then we are probably low on memory, allow it to succeed
|
|
// it just means the view will not have a droptarget ....
|
|
if ( pDrop != NULL )
|
|
{
|
|
IUnknown *pUnk = NULL;
|
|
hr = this->_InternalQueryInterface( IID_IUnknown, (void **) &pUnk );
|
|
Assert( SUCCEEDED( hr ));
|
|
|
|
CoLockObjectExternal( pUnk, TRUE, FALSE );
|
|
pUnk->Release();
|
|
|
|
RegisterDragDrop( m_hWndListView, pDrop );
|
|
pDrop->Release();
|
|
}
|
|
|
|
lpParams->hwndView = m_hWnd;
|
|
|
|
FocusOnSomething();
|
|
|
|
hr = CoCreateInstance( CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC, IID_IShellTaskScheduler, (void **) & m_pScheduler );
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
#ifdef DEBUG
|
|
// provide a small timeout to ensure the thread dies and gets restarted often...
|
|
m_pScheduler->Status( ITSSFLAG_KILL_ON_DESTROY, 1000 );
|
|
#else
|
|
m_pScheduler->Status( ITSSFLAG_KILL_ON_DESTROY, 5*60*1000 );
|
|
#endif
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CThumbnailView::HandleRename(LPCITEMIDLIST pidlNew)
|
|
{
|
|
return SelectItem( pidlNew, SVSI_EDIT );
|
|
}
|
|
|
|
|
|
|
|
void CThumbnailView::CheckViewOptions()
|
|
{
|
|
DWORD dwMask = (LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE | LVS_EX_UNDERLINECOLD |
|
|
LVS_EX_UNDERLINEHOT);
|
|
|
|
DWORD dwSelection = 0;
|
|
SHELLSTATE ss;
|
|
|
|
ZeroMemory( &ss, sizeof( ss ));
|
|
|
|
SHGetSetSettings(&ss, SSF_DOUBLECLICKINWEBVIEW | SSF_SHOWALLOBJECTS | SSF_SHOWCOMPCOLOR, FALSE );
|
|
if ( !ss.fDoubleClickInWebView )
|
|
{
|
|
dwSelection = LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE;
|
|
}
|
|
|
|
m_fShowCompColor = !(!(ss.fShowCompColor));
|
|
|
|
DWORD cb;
|
|
DWORD dwUnderline = ICON_IE;
|
|
DWORD dwExStyle;
|
|
|
|
|
|
// Read the icon underline settings.
|
|
|
|
cb = SIZEOF(dwUnderline);
|
|
SHRegGetUSValueA("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
|
|
"IconUnderline",
|
|
NULL,
|
|
&dwUnderline,
|
|
&cb,
|
|
FALSE,
|
|
&dwUnderline,
|
|
cb);
|
|
|
|
|
|
// If it says to use the IE link settings, read them in.
|
|
|
|
if (dwUnderline == ICON_IE)
|
|
{
|
|
dwUnderline = ICON_YES;
|
|
|
|
TCHAR szUnderline[8];
|
|
cb = SIZEOF(szUnderline);
|
|
SHRegGetUSValueA("Software\\Microsoft\\Internet Explorer\\Main",
|
|
"Anchor Underline",
|
|
NULL,
|
|
szUnderline,
|
|
&cb,
|
|
FALSE,
|
|
szUnderline,
|
|
cb);
|
|
|
|
|
|
// Convert the string to an ICON_ value.
|
|
|
|
if (!lstrcmpiA(szUnderline, "hover"))
|
|
dwUnderline = ICON_HOVER;
|
|
else if (!lstrcmpiA(szUnderline, "no"))
|
|
dwUnderline = ICON_NO;
|
|
else
|
|
dwUnderline = ICON_YES;
|
|
}
|
|
|
|
|
|
// Convert the ICON_ value into an LVS_EX value.
|
|
|
|
switch (dwUnderline)
|
|
{
|
|
case ICON_NO:
|
|
dwExStyle = 0;
|
|
break;
|
|
|
|
case ICON_HOVER:
|
|
dwExStyle = LVS_EX_UNDERLINEHOT;
|
|
break;
|
|
|
|
case ICON_YES:
|
|
dwExStyle = LVS_EX_UNDERLINEHOT | LVS_EX_UNDERLINECOLD;
|
|
break;
|
|
}
|
|
|
|
// WPARAM is the mask, LPARAM is the new values
|
|
SendMessageA( m_hWndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, (WPARAM) dwMask,
|
|
(LPARAM) (dwSelection | dwExStyle ));
|
|
|
|
m_fShowAllObjects = ss.fShowAllObjects;
|
|
|
|
// by default we are "connected"
|
|
// for Beta2 we are offline...
|
|
m_fOffline = TRUE;
|
|
|
|
DWORD dwState, dwSize = sizeof( dwState );
|
|
|
|
// check the global online/offline mode...
|
|
if (InternetQueryOptionA(NULL, INTERNET_OPTION_CONNECTED_STATE, &dwState, &dwSize))
|
|
{
|
|
if (dwState & INTERNET_STATE_DISCONNECTED_BY_USER)
|
|
m_fOffline = TRUE;
|
|
}
|
|
|
|
// we still think we are connected, then check to see if we really have a connection
|
|
if ( !m_fOffline )
|
|
{
|
|
DWORD dwFlags;
|
|
DWORD dwMask = INTERNET_CONNECTION_MODEM | INTERNET_CONNECTION_LAN | INTERNET_CONNECTION_PROXY;
|
|
dwFlags = dwMask;
|
|
if ( InternetGetConnectedState( &dwFlags, 0 ) && ( dwFlags & dwMask ))
|
|
{
|
|
// we are connected
|
|
}
|
|
else
|
|
{
|
|
// no connection even though we are no in offline mode...
|
|
m_fOffline = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// *** IShellChangeNotify methods ***
|
|
|
|
STDMETHODIMP CThumbnailView::OnChange(LONG lEvent,
|
|
LPCITEMIDLIST pidl1,
|
|
LPCITEMIDLIST pidl2)
|
|
{
|
|
LPITEMIDLIST pidlLast1 = (LPITEMIDLIST) FindLastPidl( pidl1 );
|
|
|
|
// lParam contains the notification code ...
|
|
switch ( lEvent )
|
|
{
|
|
case SHCNE_CREATE:
|
|
case SHCNE_MKDIR:
|
|
{
|
|
int iItem = FindInView( m_hWndListView, m_pFolder, pidlLast1 );
|
|
if ( iItem == -1 )
|
|
{
|
|
LPITEMIDLIST pidlNew = NULL;
|
|
SimpleIDLISTToRealIDLIST( m_pFolder, pidlLast1, &pidlNew );
|
|
if (pidlNew)
|
|
{
|
|
iItem = AddItem( pidlNew );
|
|
if (iItem < 0)
|
|
{
|
|
SHFree(pidlNew);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
UpdateStatusBar( NULL, NULL );
|
|
break;
|
|
|
|
case SHCNE_DELETE:
|
|
case SHCNE_RMDIR:
|
|
{
|
|
UINT iItem;
|
|
RemoveObject( (LPITEMIDLIST) pidlLast1, &iItem );
|
|
}
|
|
UpdateStatusBar( NULL, NULL );
|
|
break;
|
|
|
|
case SHCNE_UPDATEIMAGE:
|
|
Refresh( );
|
|
break;
|
|
|
|
case SHCNE_NETSHARE:
|
|
case SHCNE_NETUNSHARE:
|
|
case SHCNE_ATTRIBUTES:
|
|
case SHCNE_UPDATEITEM:
|
|
{
|
|
// find the listview item based on the pidl.
|
|
int iItem = FindInView( m_hWndListView, m_pFolder, pidlLast1 );
|
|
if ( iItem != -1 )
|
|
{
|
|
// now get the item info from the listview for the image index.
|
|
LV_ITEMW lvItem;
|
|
lvItem.mask = LVIF_IMAGE | LVIF_NORECOMPUTE | LVIF_PARAM;
|
|
lvItem.iItem = iItem;
|
|
lvItem.iSubItem = 0;
|
|
if ( ListView_GetItemWrapW( m_hWndListView, &lvItem ) )
|
|
{
|
|
// if the item's icon hasn't been shown yet, then there
|
|
// is no need to update the image in the icon cache.
|
|
if ( lvItem.iImage != I_IMAGECALLBACK )
|
|
{
|
|
// find out if the image is a NOUSAGE one
|
|
IMAGECACHEINFO rgInfo;
|
|
rgInfo.dwMask = ICIFLAG_NOUSAGE;
|
|
|
|
// ignore zero usage items
|
|
HRESULT hr = m_pImageCache->GetImageInfo( lvItem.iImage, &rgInfo );
|
|
if (( SUCCEEDED( hr ) && !(rgInfo.dwMask & ICIFLAG_NOUSAGE ))
|
|
|| FAILED( hr ))
|
|
{
|
|
// remove it from the thumbnail cache, forcing a re-fetch.
|
|
Assert( m_pImageCache );
|
|
m_pImageCache->DeleteImage( lvItem.iImage );
|
|
}
|
|
}
|
|
|
|
// reset the item info, so that you will get called
|
|
// back to retrieve the new image when it becomes visible.
|
|
lvItem.mask = LVIF_IMAGE;
|
|
lvItem.iImage = I_IMAGECALLBACK;
|
|
|
|
// check the items state
|
|
LPITEMIDLIST pidl = (LPITEMIDLIST) lvItem.lParam;
|
|
LPITEMIDLIST pidlNew = NULL;
|
|
SimpleIDLISTToRealIDLIST( m_pFolder, pidlLast1, &pidlNew );
|
|
if ( pidlNew != NULL )
|
|
{
|
|
lvItem.lParam = (LPARAM) pidlNew;
|
|
lvItem.mask |= LVIF_PARAM;
|
|
|
|
ULONG ulAttrs = SFGAO_GHOSTED;
|
|
HRESULT hr = m_pFolder->GetAttributesOf( 1, (LPCITEMIDLIST *)&pidlNew, &ulAttrs );
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
lvItem.stateMask = LVIS_CUT;
|
|
lvItem.state = ((ulAttrs & SFGAO_GHOSTED) ? LVIS_CUT : 0);
|
|
lvItem.mask |= LVIF_STATE;
|
|
}
|
|
}
|
|
|
|
ListView_SetItemWrapW(m_hWndListView, &lvItem );
|
|
|
|
if ( pidlNew )
|
|
{
|
|
SHFree((LPITEMIDLIST) pidl );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SHCNE_RENAMEITEM:
|
|
case SHCNE_RENAMEFOLDER:
|
|
{
|
|
// need to detect if it has been renamed into this folder, or out of.....
|
|
LPITEMIDLIST pidlLast = (LPITEMIDLIST) FindLastPidl( pidl2 );
|
|
USHORT cbSize = pidlLast->mkid.cb;
|
|
BOOL fStartedHere = FALSE;
|
|
BOOL fEndedHere = FALSE;
|
|
UINT iItem;
|
|
|
|
// remove the last item for now...
|
|
pidlLast->mkid.cb = 0;
|
|
|
|
IShellFolder *pDesktop;
|
|
HRESULT hr = SHGetDesktopFolder( &pDesktop );
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
// see if it landed here...
|
|
hr = pDesktop->CompareIDs( 0, m_pidl, pidl2 );
|
|
pidlLast->mkid.cb = cbSize;
|
|
if ( hr == 0 )
|
|
{
|
|
fEndedHere = TRUE;
|
|
}
|
|
|
|
// see if it started in this folder...
|
|
cbSize = pidlLast1->mkid.cb;
|
|
pidlLast1->mkid.cb = 0;
|
|
hr = pDesktop->CompareIDs( 0, m_pidl, pidl1 );
|
|
pidlLast1->mkid.cb = cbSize;
|
|
if ( hr == 0 )
|
|
{
|
|
fStartedHere = TRUE;
|
|
}
|
|
|
|
if ( fEndedHere )
|
|
{
|
|
hr = E_FAIL;
|
|
LPITEMIDLIST pidlNew = NULL;
|
|
SimpleIDLISTToRealIDLIST( m_pFolder, pidlLast, &pidlNew );
|
|
if ( pidlNew )
|
|
{
|
|
if ( fStartedHere )
|
|
{
|
|
hr = UpdateObject( pidlLast1, pidlNew, &iItem );
|
|
}
|
|
else
|
|
{
|
|
if (FindInView( m_hWndListView, m_pFolder, pidlNew ) == -1 )
|
|
{
|
|
if (AddItem( pidlNew ) != -1)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( hr != S_OK )
|
|
{
|
|
SHFree( pidlNew );
|
|
}
|
|
}
|
|
}
|
|
else if ( fStartedHere )
|
|
{
|
|
RemoveObject( pidlLast1, &iItem );
|
|
}
|
|
}
|
|
}
|
|
UpdateStatusBar( NULL, NULL );
|
|
break;
|
|
|
|
default:
|
|
{
|
|
LPRUNNABLETASK pTask;
|
|
HRESULT hr = CUpdateDirTask_Create( this, &pTask );
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
Assert( m_pScheduler );
|
|
m_pScheduler->RemoveTasks( TOID_UpdateDirHandler, ITSAT_DEFAULT_LPARAM, FALSE );
|
|
|
|
// add with a low prority, but higher than HTML extraction...
|
|
hr = m_pScheduler->AddTask( pTask, TOID_UpdateDirHandler, ITSAT_DEFAULT_LPARAM, 0x1000000 );
|
|
if ( hr != S_OK )
|
|
{
|
|
// run on the current thread ... REAL SLOW.....
|
|
pTask->Run();
|
|
}
|
|
pTask->Release();
|
|
}
|
|
|
|
UpdateStatusBar( NULL, NULL );
|
|
}
|
|
break;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CThumbnailView::SelectAndPositionItem(LPCITEMIDLIST pidlItem,
|
|
UINT uFlags, POINT* ppt)
|
|
{
|
|
// See if we should first deselect everything else
|
|
if (!pidlItem)
|
|
{
|
|
if (uFlags != SVSI_DESELECTOTHERS)
|
|
{
|
|
// I only know how to deselect everything
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
ListView_SetItemState(this->m_hWndListView, -1, 0, LVIS_SELECTED);
|
|
return(S_OK);
|
|
}
|
|
|
|
if (uFlags & SVSI_TRANSLATEPT)
|
|
{
|
|
//The caller is asking us to take this point and convert it from screen Coords
|
|
// to the Client of the Listview.
|
|
ScreenToClient(m_hWndListView, ppt);
|
|
|
|
POINT ptOrigin;
|
|
|
|
if (ListView_GetOrigin(m_hWndListView, &ptOrigin))
|
|
{
|
|
ppt->x += ptOrigin.x;
|
|
ppt->y += ptOrigin.y;
|
|
}
|
|
}
|
|
|
|
int i = FindItem(pidlItem);
|
|
if (i != -1)
|
|
{
|
|
// set the position first so that the ensure visible scrolls to
|
|
// the new position
|
|
if (ppt)
|
|
{
|
|
ListView_SetItemPosition32(this->m_hWndListView, i, ppt->x, ppt->y);
|
|
}
|
|
|
|
// The SVSI_EDIT flag also contains SVSI_SELECT and as such
|
|
// a simple & wont work!
|
|
if ((uFlags & SVSI_EDIT) == SVSI_EDIT)
|
|
{
|
|
// Grab focus if the listview (or any of it's children) don't already have focus
|
|
HWND hwndFocus = GetFocus();
|
|
if (!hwndFocus ||
|
|
!(hwndFocus == this->m_hWndListView || IsChild(this->m_hWndListView, hwndFocus)))
|
|
{
|
|
SetFocus(this->m_hWndListView);
|
|
}
|
|
ListView_EditLabel(this->m_hWndListView, i);
|
|
}
|
|
else
|
|
{
|
|
UINT stateMask = LVIS_SELECTED;
|
|
UINT state = (uFlags & SVSI_SELECT) ? LVIS_SELECTED : 0;
|
|
if (uFlags & SVSI_FOCUSED)
|
|
{
|
|
state |= LVIS_FOCUSED;
|
|
stateMask |= LVIS_FOCUSED;
|
|
}
|
|
|
|
// See if we should first deselect everything else
|
|
if (uFlags & SVSI_DESELECTOTHERS)
|
|
ListView_SetItemState(this->m_hWndListView, -1, 0, LVIS_SELECTED);
|
|
|
|
ListView_SetItemState(this->m_hWndListView, i, state, stateMask);
|
|
|
|
if (uFlags & SVSI_ENSUREVISIBLE)
|
|
ListView_EnsureVisible(this->m_hWndListView, i, FALSE);
|
|
|
|
SetFocus(this->m_hWndListView);
|
|
}
|
|
|
|
}
|
|
else if ((uFlags & SVSI_EDIT) == SVSI_EDIT && m_fUpdateDir )
|
|
{
|
|
// cache away the pidl until the updateDir is done
|
|
EnterCriticalSection( &m_csAddLock );
|
|
m_pidlRename = ILClone( pidlItem );
|
|
LeaveCriticalSection( &m_csAddLock );
|
|
}
|
|
return NOERROR;
|
|
}
|
|
|