3375 lines
93 KiB
C++
3375 lines
93 KiB
C++
#include "priv.h"
|
|
#include <varutil.h>
|
|
#include "sccls.h"
|
|
|
|
#include "iface.h"
|
|
#include "itbar.h"
|
|
#include "itbdrop.h"
|
|
#include "bands.h"
|
|
#include "isfband.h"
|
|
#include "menubar.h"
|
|
#include "resource.h"
|
|
#include "menuisf.h"
|
|
#include "dpastuff.h"
|
|
#include "shlwapi.h"
|
|
#include "cobjsafe.h"
|
|
#include <iimgctx.h>
|
|
#include "uemapp.h"
|
|
#include "mnfolder.h"
|
|
#include "channel.h"
|
|
#include "browmenu.h"
|
|
|
|
|
|
#define DM_VERBOSE 0 // misc verbose traces
|
|
#define DM_PERSIST 0
|
|
#define TF_BANDDD TF_BAND
|
|
#define DM_RENAME 0
|
|
#define DM_MISC 0 // miscellany
|
|
|
|
#define SZ_PROPERTIESA "properties"
|
|
#define SZ_PROPERTIES TEXT(SZ_PROPERTIESA)
|
|
#define SZ_REGKEY_ADVFOLDER TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced")
|
|
|
|
// {F47162A0-C18F-11d0-A3A5-00C04FD706EC}
|
|
static const GUID TOID_ExtractImage = { 0xf47162a0, 0xc18f, 0x11d0, { 0xa3, 0xa5, 0x0, 0xc0, 0x4f, 0xd7, 0x6, 0xec } };
|
|
|
|
|
|
#define SUPERCLASS CToolBand
|
|
|
|
HRESULT FakeGetUIObjectOf( IShellFolder *psf, LPCITEMIDLIST pidl, UINT * prgfFlags, REFIID riid, void **ppvObj );
|
|
|
|
extern UINT g_idFSNotify;
|
|
|
|
HRESULT CExtractImageTask_Create( CLogoBase *plb,
|
|
LPEXTRACTIMAGE pExtract,
|
|
LPCWSTR pszCache,
|
|
DWORD dwItem,
|
|
int iIcon,
|
|
DWORD dwFlags,
|
|
LPRUNNABLETASK * ppTask );
|
|
|
|
class CExtractImageTask : public IRunnableTask
|
|
{
|
|
public:
|
|
STDMETHOD ( QueryInterface ) ( REFIID riid, void **ppvObj );
|
|
STDMETHOD_( ULONG, AddRef ) ();
|
|
STDMETHOD_( ULONG, Release ) ();
|
|
|
|
STDMETHOD (Run)( void );
|
|
STDMETHOD (Kill)( BOOL fWait );
|
|
STDMETHOD (Suspend)( );
|
|
STDMETHOD (Resume)( );
|
|
STDMETHOD_( ULONG, IsRunning )( void );
|
|
|
|
protected:
|
|
|
|
CExtractImageTask( HRESULT * pHr,
|
|
CLogoBase *plb,
|
|
IExtractImage * pImage,
|
|
LPCWSTR pszCache,
|
|
DWORD dwItem,
|
|
int iIcon,
|
|
DWORD dwFlags );
|
|
~CExtractImageTask();
|
|
HRESULT InternalResume();
|
|
|
|
friend HRESULT CExtractImageTask_Create( CLogoBase* plb,
|
|
LPEXTRACTIMAGE pExtract,
|
|
LPCWSTR pszCache,
|
|
DWORD dwItem,
|
|
int iIcon,
|
|
DWORD dwFlags,
|
|
LPRUNNABLETASK * ppTask );
|
|
|
|
LONG m_cRef;
|
|
LONG m_lState;
|
|
LPEXTRACTIMAGE m_pExtract;
|
|
LPRUNNABLETASK m_pTask;
|
|
WCHAR m_szPath[MAX_PATH];
|
|
DWORD m_dwFlags;
|
|
DWORD m_dwItem;
|
|
CLogoBase* m_plb;
|
|
HBITMAP m_hBmp;
|
|
int m_iIcon;
|
|
};
|
|
//=================================================================
|
|
// Implementation of CISFBand
|
|
//=================================================================
|
|
|
|
|
|
CISFBand::CISFBand() : CToolbarBand()
|
|
{
|
|
_fCanFocus = TRUE;
|
|
_eUemLog = UEMIND_NIL;
|
|
_dwPriv = -1;
|
|
|
|
_fHasOrder = TRUE; // ISFBand always has an order...
|
|
_fAllowDropdown = BOOLIFY(SHRegGetBoolUSValue(SZ_REGKEY_ADVFOLDER, TEXT("CascadeFolderBands"),
|
|
FALSE,
|
|
FALSE));
|
|
|
|
// Should we enable logging of arbirary events?
|
|
// _pguidUEMGroup = &UEMIID_SHELL;
|
|
ASSERT(_pguidUEMGroup == NULL);
|
|
|
|
|
|
// Assert that this class is ZERO INITed.
|
|
ASSERT(!_pbp);
|
|
ASSERT(FALSE == _fCreatedBandProxy);
|
|
}
|
|
|
|
|
|
CISFBand::~CISFBand()
|
|
{
|
|
if(_pbp && _fCreatedBandProxy)
|
|
_pbp->SetSite(NULL);
|
|
|
|
ATOMICRELEASE(_pbp);
|
|
}
|
|
|
|
HRESULT CISFBand_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
|
|
{
|
|
// aggregation checking is handled in class factory
|
|
|
|
HRESULT hres;
|
|
CISFBand* pObj;
|
|
|
|
hres = E_OUTOFMEMORY;
|
|
|
|
pObj = new CISFBand();
|
|
if (pObj)
|
|
{
|
|
*ppunk = SAFECAST(pObj, IShellFolderBand*);
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: See CISFBand::Init for an explanation on the parameters.
|
|
|
|
*/
|
|
CISFBand* CISFBand_CreateEx(IShellFolder* psf, LPCITEMIDLIST pidl)
|
|
{
|
|
CISFBand * p = NULL;
|
|
|
|
if (psf || pidl)
|
|
{
|
|
p = new CISFBand();
|
|
if (p)
|
|
{
|
|
IShellFolderBand * psfband = SAFECAST(p, IShellFolderBand *);
|
|
if (psfband && FAILED(psfband->InitializeSFB(psf, pidl)))
|
|
{
|
|
delete p;
|
|
p = NULL;
|
|
}
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
#define _AddRef(psz) { ++_cRef; TraceMsg(TF_SHDREF, "CDocObjectView(%x)::QI(%s) is AddRefing _cRef=%d", this, psz, _cRef); }
|
|
#else
|
|
#define _AddRef(psz) ++_cRef
|
|
#endif
|
|
|
|
HRESULT CISFBand::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CISFBand, IShellFolderBand),
|
|
QITABENT(CISFBand, IFolderBandPriv),
|
|
{ 0 },
|
|
};
|
|
|
|
HRESULT hres = QISearch(this, qit, riid, ppvObj);
|
|
|
|
if (FAILED(hres))
|
|
hres = CToolBand::QueryInterface(riid, ppvObj);
|
|
|
|
if (FAILED(hres))
|
|
hres = CSFToolbar::QueryInterface(riid, ppvObj);
|
|
|
|
|
|
if (S_OK != hres)
|
|
{
|
|
// HACKHACK: this is yucko!
|
|
if (IsEqualIID(riid, CLSID_ISFBand))
|
|
{
|
|
*ppvObj = (void*)this;
|
|
_AddRef(TEXT("CLSID_ISFBand"));
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
#if 0
|
|
LPITEMIDLIST PidlFromFolderAndSubPath(int iFolder, TCHAR *pszSubPath)
|
|
{
|
|
LPITEMIDLIST pidl = NULL;
|
|
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, iFolder, &pidl))) {
|
|
if (pszSubPath) {
|
|
TCHAR szPath[MAX_PATH];
|
|
SHGetPathFromIDList(pidl, szPath);
|
|
PathCombine(szPath, szPath, pszSubPath);
|
|
ILFree(pidl);
|
|
pidl = ILCreateFromPath(szPath);
|
|
}
|
|
}
|
|
return pidl;
|
|
}
|
|
#endif
|
|
|
|
//*** ILIsParentCSIDL -- like ILIsParent, but accepts a CSIDL_* for pidl1
|
|
// NOTES
|
|
// TODO move to shlwapi (if/when idlist.c moves there)?
|
|
//
|
|
STDAPI_(BOOL) ILIsParentCSIDL(int csidl1, LPCITEMIDLIST pidl2, BOOL fImmediate)
|
|
{
|
|
LPITEMIDLIST pidlSpec;
|
|
BOOL fRet = FALSE;
|
|
|
|
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, csidl1, &pidlSpec))) {
|
|
fRet = ILIsParent(pidlSpec, pidl2, fImmediate);
|
|
ILFree(pidlSpec);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellFolderBand::InitializeSFB
|
|
|
|
- supply IShellFolder with no PIDL if you want to view some
|
|
ISF (either already instantiated from the filesystem or
|
|
some non-filesystem ISF) that you do NOT want to receive
|
|
notifies from (either from SHChangeNotify nor from
|
|
IShellChangeNotify)
|
|
|
|
- supply a PIDL with no IShellFolder for a full-blown band
|
|
looking at a shell namespace (rooted on desktop) item.
|
|
|
|
*/
|
|
HRESULT CISFBand::InitializeSFB(IShellFolder *psf, LPCITEMIDLIST pidl)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
|
|
// Did they try to add the Recycle Bin? If so we need to reject it
|
|
// for consistance reasons. We also reject the Temp. Internet Files
|
|
// for security reasons.
|
|
if (pidl && (ILIsParentCSIDL(CSIDL_BITBUCKET, pidl, FALSE) ||
|
|
ILIsParentCSIDL(CSIDL_INTERNET_CACHE, pidl, FALSE)))
|
|
{
|
|
// this will eventually show up as IDS_CANTISFBAND
|
|
TraceMsg(DM_TRACE, "cib.isfb: recycle => E_INVALIDARG");
|
|
hres = E_INVALIDARG;
|
|
}
|
|
|
|
if (SUCCEEDED(hres))
|
|
hres = CSFToolbar::SetShellFolder(psf, pidl);
|
|
if (SUCCEEDED(hres))
|
|
_AfterLoad();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellFolderBand::SetBandInfoSFB
|
|
|
|
*/
|
|
HRESULT CISFBand::SetBandInfoSFB(BANDINFOSFB * pbi)
|
|
{
|
|
ASSERT(pbi);
|
|
if (!pbi)
|
|
return E_POINTER;
|
|
|
|
if ((pbi->dwMask & ISFB_MASK_INVALID) ||
|
|
(pbi->dwMask & ISFB_MASK_VIEWMODE) && (pbi->wViewMode & ~3))
|
|
return E_INVALIDARG;
|
|
|
|
// We don't handle ISFB_MASK_SHELLFOLDER and ISFB_MASK_IDLIST
|
|
// in Set because there's a lot of work to resync pidl, psf, and
|
|
// notifcations in the toolbar. If somebody wants to do it,
|
|
// more power to ya. :)
|
|
if (pbi->dwMask & (ISFB_MASK_SHELLFOLDER | ISFB_MASK_IDLIST))
|
|
return E_INVALIDARG;
|
|
|
|
if (pbi->dwMask & ISFB_MASK_STATE)
|
|
{
|
|
if (pbi->dwStateMask & ISFB_STATE_DEBOSSED)
|
|
_fDebossed = BOOLIFY(pbi->dwState & ISFB_STATE_DEBOSSED);
|
|
if (pbi->dwStateMask & ISFB_STATE_ALLOWRENAME)
|
|
_fAllowRename = BOOLIFY(pbi->dwState & ISFB_STATE_ALLOWRENAME);
|
|
if (pbi->dwStateMask & ISFB_STATE_NOSHOWTEXT)
|
|
_fNoShowText = BOOLIFY(pbi->dwState & ISFB_STATE_NOSHOWTEXT);
|
|
if (pbi->dwStateMask & ISFB_STATE_CHANNELBAR)
|
|
_fChannels = BOOLIFY(pbi->dwState & ISFB_STATE_CHANNELBAR);
|
|
/* ISFB_STATE_NOTITLE: removed 970619, use cbs::SetBandState */
|
|
if (pbi->dwStateMask & ISFB_STATE_QLINKSMODE)
|
|
_fLinksMode = BOOLIFY(pbi->dwState & ISFB_STATE_QLINKSMODE);
|
|
if (pbi->dwStateMask & ISFB_STATE_FULLOPEN)
|
|
_fFullOpen = BOOLIFY(pbi->dwState & ISFB_STATE_FULLOPEN);
|
|
if (pbi->dwStateMask & ISFB_STATE_NONAMESORT)
|
|
_fNoNameSort = BOOLIFY(pbi->dwState & ISFB_STATE_NONAMESORT);
|
|
if (pbi->dwStateMask & ISFB_STATE_BTNMINSIZE)
|
|
_fBtnMinSize = BOOLIFY(pbi->dwState & ISFB_STATE_BTNMINSIZE);
|
|
}
|
|
|
|
if (pbi->dwMask & ISFB_MASK_BKCOLOR)
|
|
{
|
|
_crBkgnd = pbi->crBkgnd;
|
|
_fHaveBkColor = TRUE;
|
|
if (EVAL(_hwndTB))
|
|
SHSetWindowBits(_hwndTB, GWL_STYLE, TBSTYLE_CUSTOMERASE, TBSTYLE_CUSTOMERASE);
|
|
|
|
ASSERT(_hwnd);
|
|
|
|
if (_hwndPager)
|
|
{
|
|
TraceMsg(TF_BAND, "cib.sbisfb: Pager_SetBkColor(_hwnd=%x crBkgnd=%x)", _hwnd, _crBkgnd);
|
|
Pager_SetBkColor(_hwnd, _crBkgnd);
|
|
}
|
|
}
|
|
|
|
// BUGBUG (kkahl): We don't support changing these once TB is created
|
|
if (pbi->dwMask & ISFB_MASK_COLORS)
|
|
{
|
|
_crBtnLt = pbi->crBtnLt;
|
|
_crBtnDk = pbi->crBtnDk;
|
|
_fHaveColors = TRUE;
|
|
}
|
|
|
|
if (pbi->dwMask & ISFB_MASK_VIEWMODE)
|
|
{
|
|
_uIconSize = (pbi->wViewMode & 3); // stored in a 2-bit field currently...
|
|
|
|
// only force no recalc if one of the recalcable fields was set
|
|
_fNoRecalcDefaults = TRUE;
|
|
}
|
|
|
|
|
|
// If the bandsite queried us before, let it know the info may have changed
|
|
if (_fInitialized)
|
|
_BandInfoChanged();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellFolderBand::GetBandInfoSFB
|
|
|
|
*/
|
|
HRESULT CISFBand::GetBandInfoSFB(BANDINFOSFB * pbi)
|
|
{
|
|
ASSERT(pbi);
|
|
if (!pbi)
|
|
return E_POINTER;
|
|
|
|
if (pbi->dwMask & ISFB_MASK_STATE)
|
|
{
|
|
pbi->dwState = 0;
|
|
pbi->dwStateMask = ISFB_STATE_ALL;
|
|
|
|
if (_fDebossed)
|
|
pbi->dwState |= ISFB_STATE_DEBOSSED;
|
|
if (_fAllowRename)
|
|
pbi->dwState |= ISFB_STATE_ALLOWRENAME;
|
|
if (_fNoShowText)
|
|
pbi->dwState |= ISFB_STATE_NOSHOWTEXT;
|
|
if (_fLinksMode)
|
|
pbi->dwState |= ISFB_STATE_QLINKSMODE;
|
|
if (_fFullOpen)
|
|
pbi->dwState |= ISFB_STATE_FULLOPEN;
|
|
if (_fNoNameSort)
|
|
pbi->dwState |= ISFB_STATE_NONAMESORT;
|
|
if (_fBtnMinSize)
|
|
pbi->dwState |= ISFB_STATE_BTNMINSIZE;
|
|
}
|
|
|
|
if (pbi->dwMask & ISFB_MASK_BKCOLOR)
|
|
{
|
|
pbi->crBkgnd = (_fHaveBkColor) ? _crBkgnd : CLR_DEFAULT;
|
|
}
|
|
|
|
if (pbi->dwMask & ISFB_MASK_COLORS)
|
|
{
|
|
if (_fHaveColors)
|
|
{
|
|
pbi->crBtnLt = _crBtnLt;
|
|
pbi->crBtnDk = _crBtnDk;
|
|
}
|
|
else
|
|
{
|
|
pbi->crBtnLt = CLR_DEFAULT;
|
|
pbi->crBtnDk = CLR_DEFAULT;
|
|
}
|
|
}
|
|
|
|
if (pbi->dwMask & ISFB_MASK_VIEWMODE)
|
|
{
|
|
pbi->wViewMode = _uIconSize;
|
|
}
|
|
|
|
if (pbi->dwMask & ISFB_MASK_SHELLFOLDER)
|
|
{
|
|
pbi->psf = _psf;
|
|
if (pbi->psf)
|
|
pbi->psf->AddRef();
|
|
}
|
|
|
|
if (pbi->dwMask & ISFB_MASK_IDLIST)
|
|
{
|
|
if (_pidl)
|
|
pbi->pidl = ILClone(_pidl);
|
|
else
|
|
pbi->pidl = NULL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// *** IInputObject methods ***
|
|
HRESULT CISFBand::TranslateAcceleratorIO(LPMSG lpMsg)
|
|
{
|
|
if (SendMessage(_hwnd, TB_TRANSLATEACCELERATOR, 0, (LPARAM)lpMsg))
|
|
return S_OK;
|
|
|
|
return SUPERCLASS::TranslateAcceleratorIO(lpMsg);
|
|
}
|
|
|
|
void CISFBand::_SetCacheMenuPopup(IMenuPopup* pmp)
|
|
{
|
|
if (!SHIsSameObject(pmp, _pmpCache)) {
|
|
_ReleaseMenuPopup(&_pmpCache);
|
|
_pmpCache = pmp;
|
|
if (_pmpCache)
|
|
_pmpCache->AddRef();
|
|
}
|
|
}
|
|
|
|
|
|
void CISFBand::_ReleaseMenuPopup(IMenuPopup** ppmp)
|
|
{
|
|
IUnknown_SetSite(*ppmp, NULL);
|
|
ATOMICRELEASE(*ppmp);
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Releases the held menu popup.
|
|
|
|
*/
|
|
void CISFBand::_ReleaseMenu()
|
|
{
|
|
if (!SHIsSameObject(_pmp, _pmpCache)) {
|
|
TraceMsg(TF_MENUBAND, "Releasing pmp %#lx", _pmp);
|
|
_ReleaseMenuPopup(&_pmp);
|
|
} else
|
|
ATOMICRELEASE(_pmp);
|
|
}
|
|
|
|
//***
|
|
// ENTRY/EXIT
|
|
// S_OK desktop browser
|
|
// S_FALSE other browser (explorer, OC, etc.)
|
|
// E_xxx not a browser at all (e.g. band asking tray)
|
|
HRESULT IsDesktopBrowser(IUnknown *punkSite)
|
|
{
|
|
HRESULT hr;
|
|
IServiceProvider *psp;
|
|
IUnknown *punk;
|
|
|
|
hr = E_FAIL;
|
|
if (SUCCEEDED(IUnknown_QueryService(punkSite, SID_STopLevelBrowser, IID_IServiceProvider, (void**)&psp))) {
|
|
hr = S_FALSE;
|
|
if (SUCCEEDED(psp->QueryInterface(SID_SShellDesktop, (void**)&punk))) {
|
|
hr = S_OK;
|
|
punk->Release();
|
|
}
|
|
psp->Release();
|
|
}
|
|
|
|
TraceMsg(DM_VERBOSE, "idb: ret hrDesk=%x (0=dt 1=sh e=!brow)", hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IDockingWindow::SetSite method.
|
|
|
|
*/
|
|
HRESULT CISFBand::SetSite(IUnknown* punkSite)
|
|
{
|
|
_ReleaseMenu();
|
|
|
|
SUPERCLASS::SetSite(punkSite);
|
|
|
|
if (_punkSite)
|
|
{
|
|
if (!_hwndTB)
|
|
_CreateToolbar(_hwndParent);
|
|
|
|
IUnknown_SetOwner(_psf, SAFECAST(this, IDeskBand*));
|
|
|
|
_Initialize(); // BUGBUG always or just on 1st SetSite?
|
|
}
|
|
else
|
|
IUnknown_SetOwner(_psf, NULL);
|
|
|
|
|
|
// BUGBUG: the below is bogus - no need to throw away and recreate.
|
|
|
|
// First destroy the band proxy
|
|
|
|
// Call SetSite(NULL) only if you own
|
|
// if not, it's the parent from whom you got it via QS who will call SetSite(NULL)
|
|
|
|
if(_pbp && _fCreatedBandProxy)
|
|
_pbp->SetSite(NULL);
|
|
|
|
ATOMICRELEASE(_pbp);
|
|
_fCreatedBandProxy = FALSE;
|
|
// Need a bandproxy
|
|
QueryService_SID_IBandProxy(punkSite, IID_IBandProxy, &_pbp, NULL);
|
|
if(!_pbp)
|
|
{
|
|
// We need to create it ourselves since our parent couldn't help
|
|
ASSERT(FALSE == _fCreatedBandProxy);
|
|
HRESULT hres;
|
|
hres = CreateIBandProxyAndSetSite(punkSite, IID_IBandProxy, &_pbp, NULL);
|
|
if(_pbp)
|
|
{
|
|
ASSERT(S_OK == hres);
|
|
_fCreatedBandProxy = TRUE;
|
|
}
|
|
}
|
|
|
|
ASSERT(_pbp);
|
|
return S_OK;
|
|
}
|
|
|
|
void CISFBand::_Initialize()
|
|
{
|
|
_fDesktop = (IsDesktopBrowser(_punkSite) == S_OK);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IDockingWindow::CloseDW method.
|
|
|
|
*/
|
|
HRESULT CISFBand::CloseDW(DWORD dw)
|
|
{
|
|
_fClosing = TRUE;
|
|
|
|
// close down the task scheduler ...
|
|
if ( _pTaskScheduler )
|
|
ATOMICRELEASE( _pTaskScheduler );
|
|
|
|
_UnregisterToolbar();
|
|
EmptyToolbar();
|
|
|
|
IUnknown_SetOwner(_psf, NULL);
|
|
_SetCacheMenuPopup(NULL);
|
|
|
|
// should get freed in EmptyToolbar();
|
|
ASSERT(!_hdpa);
|
|
|
|
return SUPERCLASS::CloseDW(dw);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IDockingWindow::ShowDW method
|
|
|
|
*/
|
|
HRESULT CISFBand::ShowDW(BOOL fShow)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
|
|
SUPERCLASS::ShowDW(fShow);
|
|
|
|
if (fShow)
|
|
{
|
|
_fShow = TRUE;
|
|
|
|
if (_fDirty)
|
|
{
|
|
_FillToolbar();
|
|
}
|
|
|
|
if (!_fDelayInit)
|
|
{
|
|
_RegisterToolbar();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_fShow = FALSE;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
void CISFBand::_StopDelayPainting()
|
|
{
|
|
if (_fDelayPainting) {
|
|
_fDelayPainting = FALSE;
|
|
// May be called by background thread
|
|
// Use PostMessage instead of SendMessage to avoid deadlock
|
|
PostMessage(_hwndTB, WM_SETREDRAW, TRUE, 0);
|
|
if (_hwndPager)
|
|
PostMessage(_hwnd, PGM_RECALCSIZE, 0L, 0L);
|
|
}
|
|
}
|
|
|
|
HWND CISFBand::_CreatePager(HWND hwndParent)
|
|
{
|
|
// don't create a pager for isfbands
|
|
return hwndParent;
|
|
}
|
|
|
|
void CISFBand::_CreateToolbar(HWND hwndParent)
|
|
{
|
|
if (_fHaveBkColor)
|
|
_dwStyle |= TBSTYLE_CUSTOMERASE;
|
|
CSFToolbar::_CreateToolbar(hwndParent);
|
|
if ( _fHaveBkColor )
|
|
ToolBar_SetInsertMarkColor(_hwndTB, GetSysColor( COLOR_BTNFACE ));
|
|
|
|
ASSERT(_hwndTB);
|
|
|
|
SendMessage(_hwndTB, TB_SETEXTENDEDSTYLE, TBSTYLE_EX_DRAWDDARROWS, TBSTYLE_EX_DRAWDDARROWS);
|
|
|
|
if(_fChannels)
|
|
{
|
|
SHSetWindowBits(_hwndTB, GWL_EXSTYLE, dwExStyleRTLMirrorWnd, 0);
|
|
}
|
|
|
|
_hwnd = _hwndPager ? _hwndPager : _hwndTB;
|
|
|
|
if (_fHaveColors)
|
|
{
|
|
COLORSCHEME cs;
|
|
|
|
cs.dwSize = SIZEOF(cs);
|
|
cs.clrBtnHighlight = _crBtnLt;
|
|
cs.clrBtnShadow = _crBtnDk;
|
|
SendMessage(_hwndTB, TB_SETCOLORSCHEME, 0, (LPARAM) &cs);
|
|
}
|
|
}
|
|
|
|
int CISFBand::_GetBitmap(int iCommandID, PIBDATA pibdata, BOOL fUseCache)
|
|
{
|
|
int iBitmap;
|
|
if ( _uIconSize == ISFBVIEWMODE_LOGOS )
|
|
{
|
|
LPRUNNABLETASK pTask = NULL;
|
|
DWORD dwPriority = 0;
|
|
// fetch the logo instead...
|
|
ASSERT(!_fDelayPainting);
|
|
// Warning - cannot hold ptask in a member variable - it will be a circular reference
|
|
iBitmap = GetLogoIndex( iCommandID, pibdata->GetPidl(), &pTask, &dwPriority, NULL );
|
|
if (pTask)
|
|
{
|
|
AddTaskToQueue(pTask, dwPriority, (DWORD)iCommandID);
|
|
ATOMICRELEASE(pTask);
|
|
}
|
|
}
|
|
else
|
|
iBitmap = CSFToolbar::_GetBitmap(iCommandID, pibdata, fUseCache);
|
|
|
|
return iBitmap;
|
|
}
|
|
|
|
void CISFBand::_SetDirty(BOOL fDirty)
|
|
{
|
|
CSFToolbar::_SetDirty(fDirty);
|
|
|
|
if (fDirty)
|
|
IUnknown_Exec(_punkSite, &CGID_PrivCITCommands, CITIDM_SET_DIRTYBIT, TRUE, NULL, NULL);
|
|
}
|
|
|
|
BOOL CISFBand::_UpdateIconSize(UINT uIconSize, BOOL fUpdateButtons)
|
|
{
|
|
BOOL fChanged = (_uIconSize != uIconSize);
|
|
|
|
_uIconSize = uIconSize;
|
|
HIMAGELIST himl = NULL;
|
|
|
|
if ( uIconSize == ISFBVIEWMODE_LOGOS )
|
|
{
|
|
if ( SUCCEEDED( InitLogoView()))
|
|
{
|
|
himl = GetLogoHIML();
|
|
}
|
|
if ( himl )
|
|
{
|
|
SendMessage(_hwndTB, TB_SETIMAGELIST, 0, (LPARAM)himl);
|
|
|
|
_UpdateButtons();
|
|
}
|
|
}
|
|
|
|
if ( !himl )
|
|
fChanged |= CSFToolbar::_UpdateIconSize(uIconSize,fUpdateButtons);
|
|
return fChanged;
|
|
}
|
|
|
|
void CISFBand::_UpdateVerticalMode(BOOL fVertical)
|
|
{
|
|
_fVertical = (fVertical != 0);
|
|
|
|
TraceMsg(TF_BAND, "ISFBand::_UpdateVerticalMode going %hs", _fVertical ? "VERTICAL" : "HORIZONTAL");
|
|
|
|
ASSERT(_hwnd);
|
|
|
|
if (_hwndPager) {
|
|
SHSetWindowBits(_hwnd, GWL_STYLE, PGS_HORZ|PGS_VERT,
|
|
_fVertical ? PGS_VERT : PGS_HORZ);
|
|
}
|
|
|
|
if (_hwndTB)
|
|
{
|
|
SHSetWindowBits(_hwndTB, GWL_STYLE, TBSTYLE_WRAPABLE | CCS_VERT,
|
|
TBSTYLE_WRAPABLE | (_fVertical ? CCS_VERT : 0));
|
|
}
|
|
}
|
|
|
|
HRESULT IUnknown_QueryBand(IUnknown *punk, DWORD dwBandID, IDeskBand** ppstb, DWORD* pdwState, LPWSTR pszName, int cchName)
|
|
{
|
|
HRESULT hr;
|
|
IBandSite *pbs;
|
|
|
|
hr = punk->QueryInterface(IID_IBandSite, (void**)&pbs);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = pbs->QueryBand(dwBandID, ppstb, pdwState, pszName, cchName);
|
|
pbs->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
#define CISFBAND_GETBUTTONSIZE() (_hwndTB ? (LONG)SendMessage(_hwndTB, TB_GETBUTTONSIZE, 0, 0L) : MAKELONG(16, 16))
|
|
|
|
//
|
|
// _GetIdealSize
|
|
//
|
|
// calculates ideal height and width for band and passes back in
|
|
// psize, if psize isn't NULL; return value is band's 'ideal length'
|
|
// (ideal height if vertical, else ideal width)
|
|
//
|
|
int CISFBand::_GetIdealSize(PSIZE psize)
|
|
{
|
|
SIZE size;
|
|
LONG lButtonSize = CISFBAND_GETBUTTONSIZE();
|
|
RECT rc = {0};
|
|
if (_hwndTB)
|
|
GetClientRect(_hwndTB, &rc);
|
|
|
|
if (_fVertical)
|
|
{
|
|
// set width to be max of toolbar width and toolbar button width
|
|
size.cx = max(RECTWIDTH(rc), LOWORD(lButtonSize));
|
|
// have toolbar calculate height given that width
|
|
SendMessage(_hwndTB, TB_GETIDEALSIZE, TRUE, (LPARAM)&size);
|
|
}
|
|
else
|
|
{
|
|
// set height to be max of toolbar width and toolbar button width
|
|
size.cy = max(RECTHEIGHT(rc), HIWORD(lButtonSize));
|
|
// have toolbar calculate width given that height
|
|
SendMessage(_hwndTB, TB_GETIDEALSIZE, FALSE, (LPARAM)&size);
|
|
}
|
|
|
|
// BUGBUG: I'm ripping out this check as it causes nt5 bug #225449 (disappearing chevron).
|
|
// _fDirty == TRUE doesn't mean "we're still waiting to call _FillToolbar", it just means
|
|
// "we need to persist out this order stream". The bit gets set after a drag-and-drop
|
|
// reordering, but we don't call a matching _FillToolbar in that case.
|
|
#if 0
|
|
if (_fDirty)
|
|
{
|
|
// until the TB is populated, we get back bogus data from the
|
|
// above. so use -1 until we actually have a correct answer.
|
|
size.cx = size.cy = -1;
|
|
}
|
|
#endif
|
|
|
|
if (psize)
|
|
*psize = size;
|
|
return _fVertical ? size.cy : size.cx;
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IDeskBand::GetBandInfo method
|
|
|
|
*/
|
|
|
|
HRESULT CISFBand::GetBandInfo(DWORD dwBandID, DWORD fViewMode,
|
|
DESKBANDINFO* pdbi)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
_dwBandID = dwBandID;
|
|
// We don't know the default icon size until GetBandInfo is called.
|
|
// After we set the default, we pay attention to the context menu.
|
|
//
|
|
if (!_fNoRecalcDefaults)
|
|
{
|
|
_uIconSize = (fViewMode & (DBIF_VIEWMODE_FLOATING |DBIF_VIEWMODE_VERTICAL)) ? ISFBVIEWMODE_LARGEICONS : ISFBVIEWMODE_SMALLICONS;
|
|
_fNoRecalcDefaults = TRUE;
|
|
}
|
|
|
|
if (!_fInitialized) {
|
|
_fInitialized = TRUE;
|
|
_UpdateIconSize(_uIconSize, FALSE);
|
|
_UpdateShowText(_fNoShowText);
|
|
}
|
|
|
|
// we treat floating the same as vertical
|
|
_UpdateVerticalMode(fViewMode & (DBIF_VIEWMODE_FLOATING |DBIF_VIEWMODE_VERTICAL));
|
|
|
|
LONG lButtonSize = CISFBAND_GETBUTTONSIZE();
|
|
|
|
pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT | DBIMF_USECHEVRON;
|
|
if (_fDebossed)
|
|
pdbi->dwModeFlags |= DBIMF_DEBOSSED;
|
|
|
|
pdbi->ptMinSize.x = 0;
|
|
pdbi->ptMaxSize.y = 32000; // random
|
|
pdbi->ptIntegral.y = 1;
|
|
pdbi->ptIntegral.x = 1;
|
|
|
|
if (!_fFullOpen)
|
|
_iIdealLength = _GetIdealSize((PSIZE)&pdbi->ptActual);
|
|
|
|
// CalcMinWidthHeight {
|
|
// BUGBUG need pager msg for cx/cy scroll
|
|
#define g_cxScrollbar (GetSystemMetrics(SM_CXVSCROLL) * 3 / 4)
|
|
#define g_cyScrollbar (GetSystemMetrics(SM_CYVSCROLL) * 3 / 4)
|
|
#define CX_TBBUTTON_MAX (16 + CX_FILENAME_AVG) // button + name
|
|
#define CY_TBBUTTON_MAX (16) // button
|
|
|
|
int csBut, csButMin, clBut, clButMin, clScroll;
|
|
|
|
// set up short/long aliases
|
|
if (_fVertical) {
|
|
csBut = LOWORD(lButtonSize);
|
|
if (_fBtnMinSize)
|
|
csButMin = min(csBut, CX_TBBUTTON_MAX);
|
|
else
|
|
csButMin = 0; // people like to shrink things way down, so let 'em
|
|
|
|
clBut = HIWORD(lButtonSize);
|
|
clButMin = clBut;
|
|
//ASSERT(min(clBut, CY_TBBUTTON_MAX) == clButMin); // fails!
|
|
|
|
clScroll = g_cyScrollbar;
|
|
}
|
|
else {
|
|
csBut = HIWORD(lButtonSize);
|
|
csButMin = csBut;
|
|
//ASSERT(min(csBut, CY_TBBUTTON_MAX) == csButMin); // fails!
|
|
|
|
clBut = LOWORD(lButtonSize);
|
|
clButMin = min(clBut, CX_TBBUTTON_MAX);
|
|
|
|
clScroll = g_cxScrollbar;
|
|
|
|
// nt5:176448: integral for horz
|
|
//pdbi->ptIntegral.y = csBut; this is the cause for 287082 and 341592
|
|
}
|
|
|
|
// n.b. virt pdbi->pt.x,y is really phys y,x (i.e. phys long,short)
|
|
pdbi->ptMinSize.x = 0;
|
|
pdbi->ptMinSize.y = csButMin;
|
|
|
|
DWORD dwState = BSSF_NOTITLE;
|
|
IUnknown_QueryBand(_punkSite, dwBandID, NULL, &dwState, NULL, 0);
|
|
if (dwState & BSSF_NOTITLE) { // _fNoTitle
|
|
int i, cBut, clTmp;
|
|
|
|
// cbut= text notext
|
|
// horz 1 4
|
|
// vert 1 1
|
|
cBut = 1;
|
|
if (!_fVertical && _fNoShowText) {
|
|
// special-case for QLaunch so see several buttons
|
|
cBut = 4; // for both QLaunch and arbitrary ISF band
|
|
}
|
|
|
|
pdbi->ptMinSize.x = cBut * clButMin;
|
|
|
|
if (_hwndPager) {
|
|
// tack on extra space for pager arrows
|
|
pdbi->ptMinSize.x += 2 * clScroll;
|
|
}
|
|
|
|
i = (int)SendMessage(_hwndTB, TB_BUTTONCOUNT, 0, 0);
|
|
if (i <= cBut) {
|
|
clTmp = i * clBut;
|
|
if (clTmp < pdbi->ptMinSize.x) {
|
|
// scrollbars take as much space as button would
|
|
// so just do the button
|
|
pdbi->ptMinSize.x = clTmp;
|
|
}
|
|
}
|
|
}
|
|
// }
|
|
|
|
#if 0 // BUGBUG don't we need this?
|
|
if (_fHaveBkColor) {
|
|
pdbi->crBkgnd = _crBkgnd;
|
|
pdbi->dwModeFlags |= DBIMF_BKCOLOR;
|
|
}
|
|
#endif
|
|
|
|
hr = _GetTitleW(pdbi->wszTitle, ARRAYSIZE(pdbi->wszTitle));
|
|
if (FAILED(hr))
|
|
{
|
|
// we don't support title
|
|
#ifdef DEBUG
|
|
if (pdbi->dwMask & DBIM_TITLE)
|
|
TraceMsg(DM_VERBOSE, "cisfb.gbi: patch ~DBIM_TITLE");
|
|
#endif
|
|
pdbi->dwMask &= ~DBIM_TITLE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
LRESULT CISFBand::_OnCustomDraw(NMCUSTOMDRAW* pnmcd)
|
|
{
|
|
NMTBCUSTOMDRAW * ptbcd = (NMTBCUSTOMDRAW *)pnmcd;
|
|
LRESULT lres = CDRF_DODEFAULT;
|
|
|
|
switch (pnmcd->dwDrawStage)
|
|
{
|
|
case CDDS_PREPAINT:
|
|
// if there is a palette, then quietly select it into the DC ...
|
|
if ( _hpalHalftone && _uIconSize == ISFBVIEWMODE_LOGOS )
|
|
{
|
|
ASSERT( pnmcd->hdc );
|
|
_hpalOld = SelectPalette( pnmcd->hdc, _hpalHalftone, TRUE );
|
|
// LINTASSERT(_hpalOld || !_hpalOld); // 0 semi-ok for SelectPalette
|
|
RealizePalette( pnmcd->hdc );
|
|
}
|
|
|
|
// make sure we get the postpaint as well so we can de-select the palette...
|
|
lres = CDRF_NOTIFYPOSTPAINT;
|
|
break;
|
|
|
|
case CDDS_POSTPAINT:
|
|
// if there is a palette, then quietly select it into the DC ...
|
|
if ( _hpalHalftone && _uIconSize == ISFBVIEWMODE_LOGOS )
|
|
{
|
|
ASSERT( pnmcd->hdc );
|
|
(void) SelectPalette( pnmcd->hdc, _hpalOld, TRUE );
|
|
// we don't need a realize here, we can keep the other palette realzied, we
|
|
// re select the old palette above, otherwise we bleed the resource....
|
|
// RealizePalette( pnmcd->hdc );
|
|
}
|
|
break;
|
|
|
|
case CDDS_PREERASE:
|
|
if (_fHaveBkColor)
|
|
{
|
|
RECT rcClient;
|
|
GetClientRect(_hwndTB, &rcClient);
|
|
SHFillRectClr(pnmcd->hdc, &rcClient, _crBkgnd);
|
|
lres = CDRF_SKIPDEFAULT;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return lres;
|
|
}
|
|
|
|
void CISFBand::_OnDragBegin(int iItem, DWORD dwPreferedEffect)
|
|
{
|
|
LPCITEMIDLIST pidl = _IDToPidl(iItem, &_iDragSource);
|
|
ToolBar_MarkButton(_hwndTB, iItem, TRUE);
|
|
|
|
DragDrop(_hwnd, _psf, pidl, dwPreferedEffect, NULL);
|
|
|
|
ToolBar_MarkButton(_hwndTB, iItem, FALSE);
|
|
_iDragSource = -1;
|
|
}
|
|
|
|
LRESULT CISFBand::_OnHotItemChange(NMTBHOTITEM * pnm)
|
|
{
|
|
LPNMTBHOTITEM lpnmhi = (LPNMTBHOTITEM)pnm;
|
|
LRESULT lres = 0;
|
|
|
|
if (_hwndPager && (lpnmhi->dwFlags & HICF_ARROWKEYS))
|
|
{
|
|
int iOldPos, iNewPos;
|
|
RECT rc, rcPager;
|
|
int heightPager;
|
|
|
|
int iSelected = lpnmhi->idNew;
|
|
iOldPos = (int)SendMessage(_hwnd, PGM_GETPOS, (WPARAM)0, (LPARAM)0);
|
|
iNewPos = iOldPos;
|
|
SendMessage(_hwndTB, TB_GETITEMRECT, (WPARAM)iSelected, (LPARAM)&rc);
|
|
|
|
if (rc.top < iOldPos)
|
|
{
|
|
iNewPos =rc.top;
|
|
}
|
|
|
|
GetClientRect(_hwnd, &rcPager);
|
|
heightPager = RECTHEIGHT(rcPager);
|
|
|
|
if (rc.top >= iOldPos + heightPager)
|
|
{
|
|
iNewPos += (rc.bottom - (iOldPos + heightPager)) ;
|
|
}
|
|
|
|
if (iNewPos != iOldPos)
|
|
SendMessage(_hwnd, PGM_SETPOS, (WPARAM)0, (LPARAM)iNewPos);
|
|
}
|
|
else
|
|
{
|
|
lres = CToolbarBand::_OnHotItemChange(pnm);
|
|
}
|
|
|
|
return lres;
|
|
}
|
|
|
|
LRESULT CISFBand::_OnNotify(LPNMHDR pnm)
|
|
{
|
|
LRESULT lres = 0;
|
|
switch (pnm->code)
|
|
{
|
|
case TBN_DROPDOWN:
|
|
{
|
|
LPNMTOOLBAR pnmtb = (LPNMTOOLBAR)pnm;
|
|
lres = TBDDRET_DEFAULT;
|
|
_DropdownItem(_IDToPidl(pnmtb->iItem), pnmtb->iItem);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
lres = CSFToolbar::_OnNotify(pnm);
|
|
}
|
|
|
|
return lres;
|
|
}
|
|
|
|
|
|
HRESULT CISFBand::_TBStyleForPidl(LPCITEMIDLIST pidl,
|
|
DWORD * pdwTBStyle, DWORD* pdwTBState, DWORD * pdwMIFFlags, int* piIcon)
|
|
{
|
|
HRESULT hres = CSFToolbar::_TBStyleForPidl(pidl, pdwTBStyle, pdwTBState, pdwMIFFlags, piIcon);
|
|
|
|
if (_fAllowDropdown &&
|
|
!_fCascadeFolder &&
|
|
((_GetAttributesOfPidl(pidl, SFGAO_FOLDER) & SFGAO_FOLDER) ||
|
|
IsBrowsableShellExt(pidl)))
|
|
{
|
|
*pdwTBStyle &= ~BTNS_BUTTON;
|
|
*pdwTBStyle |= BTNS_DROPDOWN;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
LRESULT CISFBand::_OnContextMenu(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT lres;
|
|
|
|
lres = CSFToolbar::_OnContextMenu(wParam, lParam);
|
|
|
|
// todo: csidl?
|
|
TraceMsg(DM_MISC, "cib._ocm: _dwPriv=%d", _dwPriv);
|
|
UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_UICONTEXT, (_dwPriv == CSIDL_APPDATA || _dwPriv == CSIDL_FAVORITES) ? UIBL_CTXTQCUTITEM : UIBL_CTXTISFITEM);
|
|
|
|
return lres;
|
|
}
|
|
|
|
LRESULT CISFBand::_DefWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg) {
|
|
case WM_SIZE:
|
|
// forward to toolbar
|
|
SendMessage(_hwndTB, TB_AUTOSIZE, wParam, lParam);
|
|
|
|
if (_GetIdealSize(NULL) != _iIdealLength) {
|
|
// our ideal size has changed since the last time bandsite
|
|
// asked; so tell bandsite ask us for our bandinfo again
|
|
_BandInfoChanged();
|
|
}
|
|
return 0;
|
|
}
|
|
return CSFToolbar::_DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Set the given IMenuPopup as the submenu to expand. Returns
|
|
S_FALSE if the menu was modal, S_OK if it was modeless, or
|
|
failure.
|
|
|
|
*/
|
|
HRESULT CISFBand::_SetSubMenuPopup(IMenuPopup* pmp, UINT uiCmd, LPCITEMIDLIST pidl, DWORD dwFlagsMPPF)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
|
|
_ReleaseMenu();
|
|
|
|
_pmp = pmp;
|
|
|
|
if (pmp) {
|
|
|
|
pmp->AddRef();
|
|
|
|
RECT rc;
|
|
POINT pt;
|
|
|
|
SendMessage(_hwndTB, TB_GETRECT, uiCmd, (LPARAM)&rc);
|
|
MapWindowPoints(_hwndTB, HWND_DESKTOP, (POINT*)&rc, 2);
|
|
|
|
// Align the sub menu appropriately
|
|
if (_fVertical) {
|
|
pt.x = rc.right;
|
|
pt.y = rc.top;
|
|
} else {
|
|
pt.x = rc.left;
|
|
pt.y = rc.bottom;
|
|
}
|
|
|
|
//
|
|
// Use a reflect point for the sub-menu to start
|
|
// if the window is RTL mirrored. [samera]
|
|
//
|
|
if (IS_WINDOW_RTL_MIRRORED(_hwndTB)) {
|
|
pt.x = (_fVertical) ? rc.left : rc.right;
|
|
}
|
|
|
|
// Tell the sub menu deskbar who we are, so it can
|
|
// inform us later when the user navigates out of
|
|
// its scope.
|
|
IUnknown_SetSite(_pmp, SAFECAST(this, IDeskBand*));
|
|
|
|
// This must be called after SetSite is done above
|
|
_SendInitMenuPopup(pmp, pidl);
|
|
|
|
// Show the menubar
|
|
hres = _pmp->Popup((POINTL*)&pt, (RECTL*)&rc, dwFlagsMPPF);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
void CISFBand::_SendInitMenuPopup(IMenuPopup * pmp, LPCITEMIDLIST pidl)
|
|
{
|
|
}
|
|
|
|
IMenuPopup* ISFBandCreateMenuPopup(IUnknown *punk, IShellFolder* psf, LPCITEMIDLIST pidl, BANDINFOSFB * pbi, BOOL bMenuBand)
|
|
{
|
|
return ISFBandCreateMenuPopup2(punk, NULL, psf, pidl, pbi, bMenuBand);
|
|
}
|
|
|
|
|
|
IMenuPopup* ISFBandCreateMenuPopup2(IUnknown *punk, IMenuBand* pmb, IShellFolder* psf, LPCITEMIDLIST pidl, BANDINFOSFB * pbi, BOOL bMenuBand)
|
|
{
|
|
IMenuPopup* pmpParent = NULL;
|
|
VARIANTARG v = {0};
|
|
BOOL fUseCache = FALSE;
|
|
|
|
if (punk && pidl) {
|
|
fUseCache = TRUE;
|
|
IUnknown_Exec(punk, &CGID_ISFBand, ISFBID_CACHEPOPUP, 0, NULL, &v);
|
|
if (v.vt == VT_UNKNOWN && v.punkVal)
|
|
v.punkVal->QueryInterface(IID_IMenuPopup, (void **)&pmpParent);
|
|
}
|
|
|
|
IMenuPopup * pmp = CreateMenuPopup2(pmpParent, pmb, psf, pidl, pbi, bMenuBand);
|
|
|
|
if (fUseCache) {
|
|
// cache it now
|
|
|
|
// clear from the variant above to release v.punkVal of pmpParent
|
|
VariantClear(&v);
|
|
|
|
if (pmp) {
|
|
VariantInit(&v);
|
|
v.vt = VT_UNKNOWN;
|
|
v.punkVal = pmp;
|
|
pmp->AddRef();
|
|
IUnknown_Exec(punk, &CGID_ISFBand, ISFBID_CACHEPOPUP, 0, &v, NULL);
|
|
VariantClear(&v);
|
|
}
|
|
}
|
|
|
|
ATOMICRELEASE(pmpParent);
|
|
return pmp;
|
|
}
|
|
|
|
|
|
IMenuPopup * CISFBand::_CreateMenuPopup(
|
|
IShellFolder * psfChild,
|
|
LPCITEMIDLIST pidlFull,
|
|
BANDINFOSFB * pbi)
|
|
{
|
|
return ISFBandCreateMenuPopup(SAFECAST(this, IOleCommandTarget*), psfChild, pidlFull, pbi, FALSE);
|
|
}
|
|
|
|
HRESULT CISFBand::_DropdownItem(LPCITEMIDLIST pidl, UINT idCmd)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
if (_pidl && _psf)
|
|
{
|
|
LPITEMIDLIST pidlFull = ILCombine(_pidl, pidl);
|
|
|
|
if (pidlFull)
|
|
{
|
|
IShellFolder* psf;
|
|
|
|
if (SUCCEEDED(_psf->BindToObject(pidl, NULL, IID_IShellFolder, (void **)&psf)))
|
|
{
|
|
RECT rc;
|
|
SendMessage(_hwndTB, TB_GETRECT, idCmd, (LPARAM)&rc);
|
|
MapWindowPoints(_hwndTB, HWND_DESKTOP, (POINT*)&rc, 2);
|
|
|
|
ITrackShellMenu* ptsm;
|
|
if (SUCCEEDED(CoCreateInstance(CLSID_TrackShellMenu, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_ITrackShellMenu, (void**)&ptsm)))
|
|
{
|
|
CFavoritesCallback *pfcb = new CFavoritesCallback();
|
|
if(pfcb) {
|
|
ptsm->Initialize(pfcb, 0, 0, SMINIT_TOPLEVEL | SMINIT_VERTICAL|SMINIT_NOSETSITE);
|
|
pfcb->SetSite(_punkSite);
|
|
}
|
|
else
|
|
ptsm->Initialize(NULL, 0, 0, SMINIT_TOPLEVEL | SMINIT_VERTICAL);
|
|
|
|
|
|
if (SUCCEEDED(ptsm->SetShellFolder(psf, pidlFull, NULL, SMSET_TOP | SMSET_USEBKICONEXTRACTION)))
|
|
{
|
|
POINTL pt = {rc.left, rc.right};
|
|
hres = ptsm->Popup(_hwndTB, &pt, (RECTL*)&rc, MPPF_BOTTOM);
|
|
}
|
|
if(pfcb)
|
|
pfcb->Release();
|
|
|
|
ptsm->Release();
|
|
}
|
|
psf->Release();
|
|
}
|
|
|
|
ILFree(pidlFull);
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Try treating the pidl as a cascading menu item.
|
|
|
|
Returns: non-zero if succeeded
|
|
*/
|
|
LRESULT CISFBand::_TryCascadingItem(LPCITEMIDLIST pidl, UINT uiCmd)
|
|
{
|
|
LRESULT lRet = 0;
|
|
|
|
// Do we cascade to another submenu?
|
|
if ((GetKeyState(VK_CONTROL) < 0) || _fCascadeFolder)
|
|
{
|
|
// Is the item a browsable folder?
|
|
if ((_GetAttributesOfPidl(pidl, SFGAO_FOLDER) & SFGAO_FOLDER) ||
|
|
IsBrowsableShellExt(pidl))
|
|
{
|
|
// Yes; cascade the browsable folder as a submenu
|
|
lRet = (S_OK == _DropdownItem(pidl, uiCmd));
|
|
}
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Try just invoking the pidl
|
|
|
|
Returns: non-zero if succeeded
|
|
*/
|
|
LRESULT CISFBand::_TrySimpleInvoke(LPCITEMIDLIST pidl)
|
|
{
|
|
LRESULT lRet = 0;
|
|
|
|
if (S_OK == _pbp->IsConnected()) // Force IE
|
|
{
|
|
LPITEMIDLIST pidlDest;
|
|
|
|
if (SUCCEEDED(SHGetNavigateTarget(_psf, pidl, &pidlDest, NULL)) && pidlDest &&
|
|
ILIsWeb(pidlDest))
|
|
{
|
|
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
// We want to ensure that we first give NavFrameWithFile a chance
|
|
// since this will do the right thing if the PIDL points to a
|
|
// shortcut.
|
|
// If the PIDL is a shortcut, NavFrameWithFile will restore any
|
|
// persistence information stored in the shortcut
|
|
// if that fails - we take the default code path that simply
|
|
// uses the PIDL
|
|
lRet = SUCCEEDED(GetPathForItem(_psf, pidl, szPath, NULL)) &&
|
|
SUCCEEDED(NavFrameWithFile(szPath, (IServiceProvider *)this));
|
|
|
|
if (!lRet)
|
|
{
|
|
if (EVAL(_pbp) && (SUCCEEDED(_pbp->NavigateToPIDL(pidlDest))))
|
|
lRet = 1;
|
|
}
|
|
ILFree(pidlDest);
|
|
}
|
|
}
|
|
|
|
if (!lRet)
|
|
{
|
|
IContextMenu *pcm = (LPCONTEXTMENU)_GetUIObjectOfPidl(pidl, IID_IContextMenu);
|
|
if (pcm)
|
|
{
|
|
LPCSTR pVerb = NULL;
|
|
UINT fFlags = 0;
|
|
|
|
// If ALT double click, accelerator for "Properties..."
|
|
if (GetKeyState(VK_MENU) < 0)
|
|
{
|
|
pVerb = SZ_PROPERTIESA;
|
|
}
|
|
|
|
//
|
|
// SHIFT+dblclick does a Explore by default
|
|
//
|
|
if (GetKeyState(VK_SHIFT) < 0)
|
|
{
|
|
fFlags |= CMF_EXPLORE;
|
|
}
|
|
|
|
IContextMenu_Invoke(pcm, _hwndTB, pVerb, fFlags);
|
|
|
|
pcm->Release();
|
|
}
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Helper function to call the menubar site's IMenuPopup::OnSelect
|
|
method.
|
|
|
|
*/
|
|
HRESULT CISFBand::_SiteOnSelect(DWORD dwType)
|
|
{
|
|
IMenuPopup * pmp;
|
|
HRESULT hres = IUnknown_QueryService(_punkSite, SID_SMenuPopup, IID_IMenuPopup, (void **)&pmp);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
pmp->OnSelect(dwType);
|
|
pmp->Release();
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
LRESULT CISFBand::_OnCommand(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
UINT uiCmd = GET_WM_COMMAND_ID(wParam, lParam);
|
|
LRESULT lres = 0;
|
|
|
|
TraceMsg(TF_BAND, "_OnCommand 0x%x", uiCmd);
|
|
|
|
LPCITEMIDLIST pidl = _IDToPidl(uiCmd);
|
|
|
|
if (pidl)
|
|
{
|
|
if (_eUemLog != UEMIND_NIL)
|
|
{
|
|
// FEATURE_UASSIST should be grp,uiCmd
|
|
UEMFireEvent(&UEMIID_SHELL, UEME_UIQCUT, UEMF_XEVENT, -1, (LPARAM)-1);
|
|
}
|
|
|
|
// Only do this if we are the quick links in the browser. The derived class will set this
|
|
if (_pguidUEMGroup)
|
|
{
|
|
LPITEMIDLIST pidlFull = ILCombine(_pidl, pidl);
|
|
if (pidlFull)
|
|
{
|
|
UEMFireEvent(_pguidUEMGroup, UEME_RUNPIDL, UEMF_XEVENT, (WPARAM)_psf, (LPARAM)pidl);
|
|
SHSendChangeMenuNotify(NULL, SHCNEE_PROMOTEDITEM, 0, pidlFull);
|
|
ILFree(pidlFull);
|
|
}
|
|
}
|
|
|
|
lres = _TryCascadingItem(pidl, uiCmd);
|
|
|
|
if (!lres && _fChannels)
|
|
lres = _TryChannelSurfing(pidl);
|
|
|
|
if (!lres)
|
|
lres = _TrySimpleInvoke(pidl);
|
|
}
|
|
else
|
|
{
|
|
MessageBeep(MB_OK);
|
|
}
|
|
|
|
return(lres);
|
|
}
|
|
|
|
// *** IPersistStream
|
|
//
|
|
|
|
HRESULT CISFBand::GetClassID(CLSID *pClassID)
|
|
{
|
|
*pClassID = CLSID_ISFBand;
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// This might be a directory inside CSIDL_APPDATA that was created on
|
|
// a Win9x machine. Win9x doesn't do the special folder signature info,
|
|
// so when it shows up on NT, it's just a boring directory that now points
|
|
// to the wrong place.
|
|
//
|
|
// So if we get a bad directory, see if it's one of these corrupted
|
|
// Win9x pidls and if so, try to reconstitute the original CSIDL_APPDATA
|
|
// by searching for "Application Data".
|
|
//
|
|
|
|
void CISFBand::_FixupAppDataDirectory()
|
|
{
|
|
TCHAR szDirPath[MAX_PATH];
|
|
|
|
// We use PathFileExists to check for existence because it turns off
|
|
// hard error boxes if the target is not available (e.g., floppy not
|
|
// in drive)
|
|
|
|
if (SHGetPathFromIDList(_pidl, szDirPath) &&
|
|
!PathFileExists(szDirPath))
|
|
{
|
|
static TCHAR szBSAppData[] = TEXT("\\Application Data");
|
|
LPTSTR pszAppData;
|
|
|
|
// For every instance of "Application Data", try to graft it
|
|
// into the real CSIDL_APPDATA. If it works, run with it.
|
|
|
|
for (pszAppData = szDirPath;
|
|
pszAppData = StrStrI(pszAppData, szBSAppData);
|
|
pszAppData++)
|
|
{
|
|
// Found a candidate. The thing after "\\Application Data"
|
|
// had better be another backslash (in which case we step
|
|
// over it) or the end of the string (in which case we don't).
|
|
|
|
TCHAR szPathBuffer[MAX_PATH];
|
|
LPTSTR pszTail = pszAppData + ARRAYSIZE(szBSAppData) - 1;
|
|
|
|
// If we did our math right, we should be right after the
|
|
// "a" at the end of "Application Data".
|
|
ASSERT(pszTail[-1] == TEXT('a'));
|
|
|
|
if (pszTail[0] == TEXT('\\'))
|
|
pszTail++; // Step over separator
|
|
else if (pszTail[0] == TEXT('\0'))
|
|
{ } // at end of string; stay there
|
|
else
|
|
continue; // we were faked out; keep looking
|
|
|
|
if (SHGetSpecialFolderPath(NULL, szPathBuffer, CSIDL_APPDATA, FALSE))
|
|
{
|
|
PathCombine(szPathBuffer, szPathBuffer, pszTail);
|
|
if (PathFileExists(szPathBuffer))
|
|
{
|
|
LPITEMIDLIST pidlReal;
|
|
pidlReal = ILCreateFromPath(szPathBuffer);
|
|
if (pidlReal)
|
|
{
|
|
ILFree(_pidl);
|
|
_pidl = pidlReal;
|
|
}
|
|
ASSERT(_pidl);
|
|
break; // found it; stop looking
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef struct tagBANDISFSTREAM {
|
|
WORD wVersion; // version of this structure
|
|
WORD cbSize; // size of this structure
|
|
DWORD dwFlags; // BANDISF_ flags
|
|
DWORD dwPriv; // special folder identifier
|
|
WORD wViewMode; // small/large/logo
|
|
WORD wUnused; // For DWORD alignment
|
|
COLORREF crBkgnd; // band background color
|
|
COLORREF crBtnLt; // band button hilite color
|
|
COLORREF crBtnDk; // band button lolite color
|
|
} BANDISFSTREAM, * PBANDISFSTREAM;
|
|
|
|
#define BANDISF_VERSION 0x22
|
|
|
|
#define BANDISF_MASK_PSF 0x00000001 // TRUE if _psf is saved
|
|
#define BANDISF_BOOL_NOSHOWTEXT 0x00000002 // TRUE if _fNoShowText
|
|
#define BANDISF_BOOL_LARGEICON 0x00000004 // last used in version 0x20
|
|
#define BANDISF_MASK_PIDLASLINK 0x00000008 // TRUE if _pidl is saved as a link
|
|
#define BANDISF_UNUSED10 0x00000010 // (obsolete) was BOOL_NOTITLE
|
|
#define BANDISF_BOOL_CHANNELS 0x00000020 // TRUE if in channel mode
|
|
#define BANDISF_BOOL_ALLOWRENAME 0x00000040 // TRUE if _psf context menu should be enabled
|
|
#define BANDISF_BOOL_DEBOSSED 0x00000080 // TRUE if band should have embossed background
|
|
#define BANDISF_MASK_ORDERLIST 0x00000100 // TRUE if an order list is saved
|
|
#define BANDISF_BOOL_BKCOLOR 0x00000200 // TRUE if bk color is persisted
|
|
#define BANDISF_BOOL_FULLOPEN 0x00000400 // TRUE if band should maximize when opened
|
|
#define BANDISF_BOOL_NONAMESORT 0x00000800 // TRUE if band should _not_ sort icons by name
|
|
#define BANDISF_BOOL_BTNMINSIZE 0x00001000 // TRUE if band should report min thickness of button
|
|
#define BANDISF_BOOL_COLORS 0x00002000 // TRUE if colors are persisted
|
|
#define BANDISF_VALIDBITS 0x00003FFF
|
|
|
|
HRESULT CISFBand::Load(IStream *pstm)
|
|
{
|
|
HRESULT hres;
|
|
DWORD cbRead;
|
|
BANDISFSTREAM bisfs = {0};
|
|
|
|
// figure out what we need to load
|
|
//
|
|
// read first DWORD only (old stream format started with ONE dword)
|
|
hres = pstm->Read(&bisfs, SIZEOF(DWORD), &cbRead);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (bisfs.cbSize == 0)
|
|
{
|
|
// upgrade case, IE4 beta1 shipped this way
|
|
//
|
|
bisfs.dwFlags = *((LPDWORD)&bisfs);
|
|
bisfs.cbSize = SIZEOF(bisfs);
|
|
bisfs.wVersion = BANDISF_VERSION;
|
|
bisfs.dwPriv = -1;
|
|
bisfs.wViewMode = (bisfs.dwFlags & BANDISF_BOOL_LARGEICON) ? ISFBVIEWMODE_LARGEICONS : ISFBVIEWMODE_SMALLICONS;
|
|
}
|
|
else
|
|
{
|
|
// read rest of stream
|
|
//
|
|
DWORD dw = (DWORD)bisfs.cbSize;
|
|
if (dw > SIZEOF(bisfs))
|
|
dw = SIZEOF(bisfs);
|
|
dw -= SIZEOF(DWORD);
|
|
hres = pstm->Read(&(bisfs.dwFlags), dw, &cbRead);
|
|
if (FAILED(hres))
|
|
return(hres);
|
|
}
|
|
|
|
// HEY, DON'T BE LAME ANY MORE. When you next touch this code,
|
|
// I suggest you figure out what sizes of this structure have
|
|
// been actually shipped and only upgrade those. Also use
|
|
// the offsetof macro so you don't have to keep calculating these
|
|
// things...
|
|
|
|
// old upgrade, I don't know what state is persisted at setup time!
|
|
//
|
|
if (bisfs.cbSize == SIZEOF(bisfs) - 3*SIZEOF(COLORREF) - SIZEOF(DWORD) - SIZEOF(DWORD))
|
|
{
|
|
bisfs.dwPriv = -1;
|
|
bisfs.cbSize += SIZEOF(DWORD);
|
|
}
|
|
// most recent upgrade, this is NOT persisted in registry at setup time!!!
|
|
//
|
|
if (bisfs.cbSize == SIZEOF(bisfs) - 3*SIZEOF(COLORREF) - SIZEOF(DWORD))
|
|
{
|
|
bisfs.wViewMode = (bisfs.dwFlags & BANDISF_BOOL_LARGEICON) ? ISFBVIEWMODE_LARGEICONS : ISFBVIEWMODE_SMALLICONS;
|
|
bisfs.cbSize = SIZEOF(bisfs);
|
|
}
|
|
// upgrade from version 0x21 + crBkgnd only to 0x22
|
|
//
|
|
if (bisfs.cbSize == SIZEOF(bisfs) - 2*SIZEOF(COLORREF))
|
|
{
|
|
bisfs.cbSize = SIZEOF(bisfs);
|
|
}
|
|
// upgrade from version 0x21 to 0x22
|
|
//
|
|
if (bisfs.cbSize == SIZEOF(bisfs) - 3*SIZEOF(COLORREF))
|
|
{
|
|
bisfs.cbSize = SIZEOF(bisfs);
|
|
}
|
|
|
|
if (!EVAL(bisfs.cbSize >= SIZEOF(bisfs)))
|
|
{
|
|
return(E_FAIL);
|
|
}
|
|
ASSERT(!(bisfs.dwFlags & ~BANDISF_VALIDBITS));
|
|
|
|
if (bisfs.dwFlags & BANDISF_BOOL_NOSHOWTEXT)
|
|
_fNoShowText = TRUE;
|
|
if (bisfs.dwFlags & BANDISF_BOOL_ALLOWRENAME)
|
|
_fAllowRename = TRUE;
|
|
if (bisfs.dwFlags & BANDISF_BOOL_DEBOSSED)
|
|
_fDebossed = TRUE;
|
|
if (bisfs.dwFlags & BANDISF_BOOL_FULLOPEN)
|
|
_fFullOpen = TRUE;
|
|
if (bisfs.dwFlags & BANDISF_BOOL_NONAMESORT)
|
|
_fNoNameSort = TRUE;
|
|
if (bisfs.dwFlags & BANDISF_BOOL_BTNMINSIZE)
|
|
_fBtnMinSize = TRUE;
|
|
if (bisfs.dwFlags & BANDISF_BOOL_BKCOLOR)
|
|
{
|
|
_crBkgnd = bisfs.crBkgnd;
|
|
_fHaveBkColor = TRUE;
|
|
}
|
|
if (bisfs.dwFlags & BANDISF_BOOL_COLORS)
|
|
{
|
|
_crBtnLt = bisfs.crBtnLt;
|
|
_crBtnDk = bisfs.crBtnDk;
|
|
_fHaveColors = TRUE;
|
|
}
|
|
|
|
_dwPriv = bisfs.dwPriv;
|
|
#if 1 // BUGBUG FEATURE_UASSIST hack this should be persisted not recalc'ed
|
|
#define UEMIsLogCsidl(dwPrivID) ((dwPrivID) == CSIDL_APPDATA)
|
|
if (UEMIsLogCsidl(_dwPriv)) {
|
|
_eUemLog = UEMIND_SHELL;
|
|
}
|
|
#endif
|
|
|
|
_uIconSize = bisfs.wViewMode;
|
|
_fNoRecalcDefaults = TRUE;
|
|
|
|
if (bisfs.dwFlags & BANDISF_MASK_PIDLASLINK)
|
|
{
|
|
ASSERT(NULL==_pidl);
|
|
hres = LoadPidlAsLink(_punkSite, pstm, &_pidl);
|
|
// If we hit hits, LoadPidlAsLink() read a chuck of our data. - BryanSt
|
|
ASSERT(SUCCEEDED(hres));
|
|
|
|
// DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
|
|
// TraceMsg(TF_BAND|TF_GENERAL, "CISFBand::Load() _pidl=>%s<", Dbg_PidlStr(_pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
|
|
|
|
_FixupAppDataDirectory();
|
|
|
|
}
|
|
|
|
if (SUCCEEDED(hres) && (bisfs.dwFlags & BANDISF_MASK_PSF))
|
|
{
|
|
ASSERT(NULL == _psf);
|
|
hres = OleLoadFromStream(pstm, IID_IShellFolder, (void **)&_psf);
|
|
}
|
|
|
|
// map this to working info
|
|
//
|
|
if (SUCCEEDED(hres))
|
|
_AfterLoad();
|
|
|
|
// we need _psf before we can read the order list.
|
|
if (SUCCEEDED(hres) && (bisfs.dwFlags & BANDISF_MASK_ORDERLIST))
|
|
{
|
|
hres = OrderList_LoadFromStream(pstm, &_hdpaOrder, _psf);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// _fDropped "persists" along with the orderlist - if this flag
|
|
// is set, we assume we have a non-default ordering
|
|
_fDropped = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT SaveIsfToStream(IShellFolder *psf, IStream *pstm)
|
|
{
|
|
IPersistStream* pps;
|
|
HRESULT hres = psf->QueryInterface(IID_IPersistStream, (void **)&pps);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = OleSaveToStream(pps, pstm);
|
|
|
|
pps->Release();
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CISFBand::Save(IStream *pstm, BOOL fClearDirty)
|
|
{
|
|
IPersistStream* pps = NULL;
|
|
HRESULT hres;
|
|
BANDISFSTREAM bisfs = {0};
|
|
|
|
// figure out what we will save
|
|
//
|
|
if (_pidl)
|
|
bisfs.dwFlags |= BANDISF_MASK_PIDLASLINK;
|
|
|
|
// BUGBUG(lamadio): This case is busted. None of the IShellFolders implement IPersistStream (at least as far as
|
|
// TJ and I can see). Qhen quick links initializes, it will set the pidlQuickLinks as the _pidl. So, in the
|
|
// After load, _fPSFBandDesktop gets set to TRUE. Why? I don't know. Well, then we never attempt to persist the
|
|
// IShellFolder and we will never fail the save. We should remove this case so we don't run into this again.
|
|
if (_psf && !_fPSFBandDesktop)
|
|
bisfs.dwFlags |= BANDISF_MASK_PSF;
|
|
if (_fDropped && (_hdpa || _hdpaOrder)) // only if a drop occurred do we have non-default ordering
|
|
bisfs.dwFlags |= BANDISF_MASK_ORDERLIST;
|
|
|
|
if (_fNoShowText)
|
|
bisfs.dwFlags |= BANDISF_BOOL_NOSHOWTEXT;
|
|
if (_fAllowRename)
|
|
bisfs.dwFlags |= BANDISF_BOOL_ALLOWRENAME;
|
|
if (_fDebossed)
|
|
bisfs.dwFlags |= BANDISF_BOOL_DEBOSSED;
|
|
if (_fFullOpen)
|
|
bisfs.dwFlags |= BANDISF_BOOL_FULLOPEN;
|
|
if (_fNoNameSort)
|
|
bisfs.dwFlags |= BANDISF_BOOL_NONAMESORT;
|
|
if (_fBtnMinSize)
|
|
bisfs.dwFlags |= BANDISF_BOOL_BTNMINSIZE;
|
|
if (_fHaveBkColor)
|
|
{
|
|
bisfs.dwFlags |= BANDISF_BOOL_BKCOLOR;
|
|
bisfs.crBkgnd = _crBkgnd;
|
|
}
|
|
if (_fHaveColors)
|
|
{
|
|
bisfs.dwFlags |= BANDISF_BOOL_COLORS;
|
|
bisfs.crBtnLt = _crBtnLt;
|
|
bisfs.crBtnDk = _crBtnDk;
|
|
}
|
|
|
|
bisfs.cbSize = SIZEOF(bisfs);
|
|
bisfs.wVersion = BANDISF_VERSION;
|
|
bisfs.dwPriv = _dwPriv;
|
|
bisfs.wViewMode = _uIconSize;
|
|
|
|
// now save it
|
|
//
|
|
hres = pstm->Write(&bisfs, SIZEOF(bisfs), NULL);
|
|
|
|
if (SUCCEEDED(hres) && bisfs.dwFlags & BANDISF_MASK_PIDLASLINK)
|
|
{
|
|
hres = SavePidlAsLink(_punkSite, pstm, _pidl);
|
|
// BUGBUG: We need to save a terminator.
|
|
}
|
|
|
|
if (SUCCEEDED(hres) && bisfs.dwFlags & BANDISF_MASK_PSF)
|
|
{
|
|
hres = SaveIsfToStream(_psf, pstm);
|
|
}
|
|
|
|
if (SUCCEEDED(hres) && (bisfs.dwFlags & BANDISF_MASK_ORDERLIST))
|
|
{
|
|
hres = OrderList_SaveToStream(pstm, (_hdpa ? _hdpa : _hdpaOrder), _psf);
|
|
}
|
|
|
|
|
|
return(hres);
|
|
}
|
|
|
|
#if 0
|
|
// IPersistPropertyBag implementation
|
|
//
|
|
HRESULT CISFBand::Load(IPropertyBag *pPropBag, IErrorLog *pErrorLog)
|
|
{
|
|
ASSERT(0); // obsolete!
|
|
_fCascadeFolder = PropBag_ReadInt4(pPropBag, L"Cascade", FALSE);
|
|
// n.b. old "Title" property nuked
|
|
_uIconSize = (PropBag_ReadInt4(pPropBag, L"Large", TRUE) ? ISFBVIEWMODE_LARGEICONS : ISFBVIEWMODE_SMALLICONS);
|
|
_fNoShowText = PropBag_ReadInt4(pPropBag, L"Text", TRUE);
|
|
|
|
return(S_OK);
|
|
}
|
|
HRESULT CISFBand::Save(IPropertyBag *pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties)
|
|
{
|
|
return(E_NOTIMPL);
|
|
}
|
|
HRESULT CISFBand::InitNew()
|
|
{
|
|
ASSERT(0); // obsolete!
|
|
return(E_NOTIMPL);
|
|
}
|
|
#endif
|
|
|
|
// IContextMenu implementation
|
|
//
|
|
HRESULT CISFBand::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
|
|
{
|
|
BOOL fChanged = FALSE;
|
|
int idCmd = -1;
|
|
|
|
UINT uNewMode = 0;
|
|
if (!HIWORD(lpici->lpVerb))
|
|
idCmd = LOWORD(lpici->lpVerb);
|
|
switch (idCmd)
|
|
{
|
|
case ISFBIDM_LARGE:
|
|
uNewMode = ISFBVIEWMODE_LARGEICONS;
|
|
goto newViewMode;
|
|
|
|
case ISFBIDM_SMALL:
|
|
uNewMode = ISFBVIEWMODE_SMALLICONS;
|
|
newViewMode:
|
|
if (uNewMode != _uIconSize)
|
|
{
|
|
BOOL fRefresh = FALSE;
|
|
|
|
if (uNewMode == ISFBVIEWMODE_LOGOS || _uIconSize == ISFBVIEWMODE_LOGOS)
|
|
{
|
|
// invalidate all before switching the imagelist...
|
|
_RememberOrder();
|
|
|
|
EmptyToolbar();
|
|
fRefresh = TRUE;
|
|
}
|
|
|
|
// we Logo view has now left the building...
|
|
if ( uNewMode != ISFBVIEWMODE_LOGOS && _uIconSize == ISFBVIEWMODE_LOGOS )
|
|
{
|
|
ExitLogoView();
|
|
}
|
|
|
|
fChanged = _UpdateIconSize(uNewMode, TRUE);
|
|
|
|
if ( fRefresh )
|
|
{
|
|
_FillToolbar();
|
|
}
|
|
if (fChanged)
|
|
_BandInfoChanged();
|
|
}
|
|
// fall thru
|
|
default:
|
|
return CSFToolbar::InvokeCommand(lpici);
|
|
}
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
// *** IOleCommandTarget methods ***
|
|
|
|
STDMETHODIMP CISFBand::QueryStatus(const GUID *pguidCmdGroup,
|
|
ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
|
|
{
|
|
HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
|
|
|
|
if (pguidCmdGroup == NULL)
|
|
{
|
|
// nothing
|
|
}
|
|
else if (IsEqualGUID(CGID_ISFBand, *pguidCmdGroup))
|
|
{
|
|
for (UINT i = 0; i < cCmds; i++)
|
|
{
|
|
switch (rgCmds[i].cmdID)
|
|
{
|
|
case ISFBID_CACHEPOPUP:
|
|
case ISFBID_ISITEMVISIBLE:
|
|
case ISFBID_PRIVATEID:
|
|
rgCmds[i].cmdf |= OLECMDF_SUPPORTED;
|
|
break;
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
}
|
|
else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
|
|
{
|
|
for (UINT i = 0; i < cCmds; i++)
|
|
{
|
|
switch (rgCmds[i].cmdID)
|
|
{
|
|
case SHDVID_UEMLOG:
|
|
rgCmds[i].cmdf |= OLECMDF_SUPPORTED;
|
|
break;
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CISFBand::_IsPidlVisible(LPITEMIDLIST pidl)
|
|
{
|
|
int i;
|
|
|
|
if (_GetButtonFromPidl(pidl, NULL, &i)) {
|
|
RECT rc;
|
|
GetClientRect(_hwndTB, &rc);
|
|
|
|
if (SHIsButtonObscured(_hwndTB, &rc, i))
|
|
return S_FALSE;
|
|
else
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CISFBand::_OrderListFromIStream(VARIANT* pvarargIn)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
if (pvarargIn->vt == VT_UNKNOWN)
|
|
{
|
|
IStream* pstm;
|
|
if (SUCCEEDED(pvarargIn->punkVal->QueryInterface(IID_IStream, (void**)&pstm)))
|
|
{
|
|
OrderList_Destroy(&_hdpaOrder);
|
|
hres = OrderList_LoadFromStream(pstm, &_hdpaOrder, _psf);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
_SetDirty(TRUE);
|
|
if (_fShow)
|
|
{
|
|
_FillToolbar();
|
|
}
|
|
}
|
|
pstm->Release();
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CISFBand::_IStreamFromOrderList(VARIANT* pvarargOut)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
ASSERT(pvarargOut != NULL);
|
|
|
|
IStream* pstm = SHCreateMemStream(NULL, 0);
|
|
if (pstm)
|
|
{
|
|
hres = OrderList_SaveToStream(pstm, _hdpa, _psf);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
pvarargOut->vt = VT_UNKNOWN;
|
|
pvarargOut->punkVal = pstm;
|
|
pvarargOut->punkVal->AddRef();
|
|
}
|
|
pstm->Release();
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
STDMETHODIMP CISFBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
|
|
DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
|
|
{
|
|
if (pguidCmdGroup == NULL)
|
|
{
|
|
// nothing
|
|
}
|
|
else if (IsEqualGUID(CGID_ISFBand, *pguidCmdGroup))
|
|
{
|
|
switch (nCmdID)
|
|
{
|
|
case ISFBID_CACHEPOPUP:
|
|
if (pvarargIn && pvarargIn->vt == VT_UNKNOWN)
|
|
{
|
|
IMenuPopup* pmp = NULL;
|
|
if (pvarargIn->punkVal)
|
|
pvarargIn->punkVal->QueryInterface(IID_IMenuPopup, (void **)&pmp);
|
|
|
|
_SetCacheMenuPopup(pmp);
|
|
|
|
ATOMICRELEASE(pmp);
|
|
}
|
|
|
|
if (pvarargOut)
|
|
{
|
|
pvarargOut->vt = VT_UNKNOWN;
|
|
pvarargOut->punkVal = _pmpCache;
|
|
if (_pmpCache)
|
|
_pmpCache->AddRef();
|
|
}
|
|
return S_OK;
|
|
|
|
case ISFBID_ISITEMVISIBLE:
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (pvarargIn && pvarargIn->vt == VT_INT_PTR)
|
|
hr = _IsPidlVisible((LPITEMIDLIST)pvarargIn->byref);
|
|
|
|
return hr;
|
|
}
|
|
|
|
case ISFBID_PRIVATEID:
|
|
// hack hack for BSMenu to differentiate between specially created
|
|
// isfbands. see bsmenu's _FindBand
|
|
// if pvarargOut is set, we give back the id we have stored.
|
|
if (pvarargOut)
|
|
{
|
|
pvarargOut->vt = VT_I4;
|
|
pvarargOut->lVal = _dwPriv;
|
|
}
|
|
// if pvarargIn is set, then we take and keep this id.
|
|
if (pvarargIn && pvarargIn->vt == VT_I4)
|
|
_dwPriv = pvarargIn->lVal;
|
|
|
|
return S_OK;
|
|
|
|
case ISFBID_GETORDERSTREAM:
|
|
return _IStreamFromOrderList(pvarargOut);
|
|
|
|
case ISFBID_SETORDERSTREAM:
|
|
return _OrderListFromIStream(pvarargIn);
|
|
}
|
|
}
|
|
else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
|
|
{
|
|
switch (nCmdID)
|
|
{
|
|
case SHDVID_UEMLOG:
|
|
ASSERT(pvarargOut == NULL);
|
|
// if pvarargIn is set, then we take and keep this id.
|
|
if (pvarargIn && pvarargIn->vt == VT_I4)
|
|
{
|
|
_eUemLog = pvarargIn->lVal;
|
|
ASSERT(_eUemLog == UEMIND_SHELL || _eUemLog == UEMIND_BROWSER);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
}
|
|
else if (IsEqualGUID(CGID_DeskBand, *pguidCmdGroup))
|
|
{
|
|
switch (nCmdID)
|
|
{
|
|
case DBID_DELAYINIT:
|
|
_fDelayInit = TRUE;
|
|
break;
|
|
|
|
case DBID_FINISHINIT:
|
|
_fDelayInit = FALSE;
|
|
_RegisterToolbar();
|
|
break;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
return OLECMDERR_E_NOTSUPPORTED;
|
|
}
|
|
|
|
IShellFolder * CISFBand::GetSF()
|
|
{
|
|
ASSERT( _psf );
|
|
return _psf;
|
|
}
|
|
|
|
HWND CISFBand::GetHWND()
|
|
{
|
|
return _hwndTB;
|
|
}
|
|
|
|
REFTASKOWNERID CISFBand::GetTOID()
|
|
{
|
|
return TOID_ExtractImage;
|
|
}
|
|
|
|
HRESULT CISFBand::OnTranslatedChange(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
if (lEvent == SHCNE_RMDIR && _IsEqualID(pidl1))
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
IBandSite *pbandSite;
|
|
if (_punkSite)
|
|
{
|
|
hres = _punkSite->QueryInterface(IID_IBandSite, (void **)&pbandSite);
|
|
if (EVAL(SUCCEEDED(hres)))
|
|
{
|
|
pbandSite->RemoveBand(_dwBandID);
|
|
pbandSite->Release();
|
|
}
|
|
}
|
|
return hres;
|
|
}
|
|
else
|
|
{
|
|
return CSFToolbar::OnTranslatedChange(lEvent, pidl1, pidl2);
|
|
}
|
|
}
|
|
|
|
HRESULT CISFBand::UpdateLogoCallback( DWORD dwItem, int iIcon, HBITMAP hImage, LPCWSTR pszCache, BOOL fCache )
|
|
{
|
|
int iItem = (int)dwItem;
|
|
HRESULT hr;
|
|
UINT uImage;
|
|
|
|
// catch if we are closing...
|
|
if ( _fClosing )
|
|
return NOERROR;
|
|
|
|
IMAGECACHEINFO rgInfo;
|
|
rgInfo.dwMask = ICIFLAG_NAME | ICIFLAG_LARGE | ICIFLAG_BITMAP | ICIFLAG_NOUSAGE;
|
|
rgInfo.cbSize = sizeof( rgInfo );
|
|
rgInfo.pszName = pszCache;
|
|
|
|
rgInfo.hBitmapLarge = hImage;
|
|
|
|
ASSERT(_pLogoCache);
|
|
if (_pLogoCache)
|
|
hr = _pLogoCache->AddImage( &rgInfo, &uImage );
|
|
else
|
|
hr = E_FAIL;
|
|
|
|
// catch if we are closing...
|
|
if ( _fClosing )
|
|
return NOERROR;
|
|
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
// remember the icon to logo mapping....
|
|
AddIndicesToLogoList( iIcon, uImage );
|
|
|
|
// catch we are closing before we try and doa bloc
|
|
PostMessage( _hwndTB, TB_CHANGEBITMAP, iItem, uImage );
|
|
}
|
|
|
|
// stop delay painting when the last extract image task calls back
|
|
if (_fDelayPainting) {
|
|
if (_pTaskScheduler && _pTaskScheduler->CountTasks(TOID_NULL) == 1) {
|
|
_StopDelayPainting();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// }
|
|
|
|
|
|
HRESULT CISFBand::_GetTitleW(LPWSTR pwszTitle, DWORD cchSize)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
TraceMsg(TF_BAND, "Calling baseclass CISFBand::_GetTitleW");
|
|
|
|
if (!EVAL(pwszTitle))
|
|
return E_INVALIDARG;
|
|
|
|
*pwszTitle = 0;
|
|
if (_pidl)
|
|
{
|
|
hr = SHGetNameAndFlagsW(_pidl, SHGDN_NORMAL, pwszTitle, cchSize, NULL);
|
|
}
|
|
else if (_psf && !_fPSFBandDesktop)
|
|
{
|
|
#ifdef BUSTED
|
|
// BUGBUG (scotth): We cannot call GetDisplayNameOf with NULL pidl.
|
|
// We must change this code so _pidl is always
|
|
// valid, and key off a flag to determine whether
|
|
// to receive notifies. Remove this code once
|
|
// that is done.
|
|
|
|
STRRET strret;
|
|
|
|
if (SUCCEEDED(_psf->GetDisplayNameOf(NULL, SHGDN_NORMAL, &strret)))
|
|
StrRetToBufW(&strret, NULL, pwszTitle, cchSize);
|
|
#endif
|
|
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDAPI NavigateToPIDL(IWebBrowser2* pwb, LPCITEMIDLIST pidl);
|
|
|
|
HRESULT FakeGetNavigateTarget(IShellFolder *psf, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl);
|
|
|
|
|
|
LRESULT CISFBand::_TryChannelSurfing(LPCITEMIDLIST pidl)
|
|
{
|
|
LRESULT lRet = 0;
|
|
|
|
ASSERT(_fChannels);
|
|
|
|
LPITEMIDLIST pidlTarget;
|
|
|
|
HRESULT hr = SHGetNavigateTarget(_psf, pidl, &pidlTarget, NULL);
|
|
|
|
// channel category folders hack.
|
|
if (FAILED(hr))
|
|
hr = FakeGetNavigateTarget(_psf, pidl, &pidlTarget);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IWebBrowser2* pwb;
|
|
|
|
// n.b. careful! only one of GCB and C_OB up the refcnt
|
|
_GetChannelBrowser(&pwb);
|
|
if (SUCCEEDED(Channels_OpenBrowser(&pwb, pwb != NULL)))
|
|
{
|
|
lRet = 1; // success at this point
|
|
|
|
if (SUCCEEDED(NavigateToPIDL(pwb, pidlTarget)))
|
|
{
|
|
LPITEMIDLIST pidlFull = ILCombine(_pidl, pidl);
|
|
if (pidlFull)
|
|
{
|
|
VARIANT varURLpidl, flags;
|
|
flags.vt = VT_I4;
|
|
flags.lVal = navBrowserBar;
|
|
if (SUCCEEDED(InitVariantFromIDList(&varURLpidl, pidlFull)))
|
|
{
|
|
pwb->Navigate2(&varURLpidl, &flags, PVAREMPTY, PVAREMPTY, PVAREMPTY);
|
|
VariantClear(&varURLpidl);
|
|
}
|
|
ILFree(pidlFull);
|
|
}
|
|
}
|
|
}
|
|
if (pwb)
|
|
pwb->Release();
|
|
|
|
ILFree(pidlTarget);
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
//*** _GetChannelBrowser -- find appropriate browser for surfing
|
|
// DESCRIPTION
|
|
// for the DTBrowser case, we fail (pwb=NULL, hr=S_FALSE) so that our
|
|
// caller will create a new SHBrowser (which can be put into theater mode).
|
|
// for the SHBrowser case, we find the top-level browser (so we'll navigate
|
|
// in-place).
|
|
HRESULT CISFBand::_GetChannelBrowser(IWebBrowser2 **ppwb)
|
|
{
|
|
HRESULT hr;
|
|
IServiceProvider *psp;
|
|
|
|
*ppwb = NULL; // assume failure
|
|
if (_fDesktop) {
|
|
ASSERT(*ppwb == NULL);
|
|
hr = S_FALSE;
|
|
}
|
|
else {
|
|
hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_IServiceProvider, (void**)&psp);
|
|
ASSERT(SUCCEEDED(hr));
|
|
if (SUCCEEDED(hr)) {
|
|
hr = psp->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, (void **)ppwb);
|
|
ASSERT(SUCCEEDED(hr));
|
|
psp->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT IUnknown_SetBandInfoSFB(IUnknown *punkBand, BANDINFOSFB *pbi)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
IShellFolderBand *pisfBand;
|
|
|
|
if (punkBand) {
|
|
hr = punkBand->QueryInterface(IID_IShellFolderBand, (void **)&pisfBand);
|
|
if (EVAL(SUCCEEDED(hr))) {
|
|
hr = pisfBand->SetBandInfoSFB(pbi);
|
|
pisfBand->Release();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////CExtractImageTask///////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// Warning
|
|
//
|
|
// The CLogoBase class cannot have a ref on the returned task
|
|
// since that would be a circular reference
|
|
//
|
|
// Warning
|
|
|
|
HRESULT CExtractImageTask_Create( CLogoBase *plb,
|
|
LPEXTRACTIMAGE pExtract,
|
|
LPCWSTR pszCache,
|
|
DWORD dwItem,
|
|
int iIcon,
|
|
DWORD dwFlags,
|
|
LPRUNNABLETASK * ppTask )
|
|
{
|
|
if ( !ppTask || !plb || !pExtract )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = NOERROR;
|
|
CExtractImageTask * pNewTask = new CExtractImageTask( &hr,
|
|
plb,
|
|
pExtract,
|
|
pszCache,
|
|
dwItem,
|
|
iIcon,
|
|
dwFlags );
|
|
if ( !pNewTask )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
if ( FAILED( hr ))
|
|
{
|
|
pNewTask->Release();
|
|
return hr;
|
|
}
|
|
|
|
*ppTask = SAFECAST( pNewTask, IRunnableTask *);
|
|
return NOERROR;
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
CExtractImageTask::CExtractImageTask( HRESULT * pHr, CLogoBase *plb, IExtractImage * pImage,
|
|
LPCWSTR pszCache, DWORD dwItem, int iIcon, DWORD dwFlags )
|
|
{
|
|
m_lState = IRTIR_TASK_NOT_RUNNING;
|
|
|
|
m_plb = plb;
|
|
m_plb->AddRef();
|
|
|
|
// cannot assume the band will kill us before it dies....
|
|
// hence we hold a reference
|
|
|
|
StrCpyW( m_szPath, pszCache );
|
|
|
|
m_pExtract = pImage;
|
|
pImage->AddRef();
|
|
|
|
m_cRef = 1;
|
|
|
|
// use the upper bit of the flags to determine if we should always call....
|
|
m_dwFlags = dwFlags;
|
|
m_dwItem = dwItem;
|
|
m_iIcon = iIcon;
|
|
|
|
// Since the task moves from thread to thread,
|
|
// don't charge this thread for the objects we're using
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
CExtractImageTask::~CExtractImageTask()
|
|
{
|
|
ATOMICRELEASE( m_pExtract );
|
|
ATOMICRELEASE( m_pTask );
|
|
|
|
if ( m_hBmp && !( m_dwFlags & EITF_SAVEBITMAP ))
|
|
{
|
|
DeleteObject( m_hBmp );
|
|
}
|
|
|
|
if(m_plb)
|
|
m_plb->Release();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP CExtractImageTask::QueryInterface( REFIID riid, void **ppvObj )
|
|
{
|
|
if ( !ppvObj )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
if ( IsEqualIID( riid, IID_IUnknown ))
|
|
{
|
|
*ppvObj = SAFECAST( this, IUnknown *);
|
|
}
|
|
else if ( IsEqualIID( riid, IID_IRunnableTask ))
|
|
{
|
|
*ppvObj = SAFECAST( this, IRunnableTask *);
|
|
}
|
|
else
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP_ (ULONG) CExtractImageTask::AddRef()
|
|
{
|
|
InterlockedIncrement( &m_cRef );
|
|
return m_cRef;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP_ (ULONG) CExtractImageTask::Release()
|
|
{
|
|
if (InterlockedDecrement( &m_cRef ) == 0 )
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_cRef;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP CExtractImageTask::Run ( void )
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
if ( m_lState == IRTIR_TASK_RUNNING )
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
else if ( m_lState == IRTIR_TASK_PENDING )
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else if ( m_lState == IRTIR_TASK_NOT_RUNNING )
|
|
{
|
|
LONG lRes = InterlockedExchange( & m_lState, IRTIR_TASK_RUNNING);
|
|
if ( lRes == IRTIR_TASK_PENDING )
|
|
{
|
|
m_lState = IRTIR_TASK_FINISHED;
|
|
return NOERROR;
|
|
}
|
|
|
|
// see if it supports IRunnableTask
|
|
m_pExtract->QueryInterface( IID_IRunnableTask, (void **) & m_pTask );
|
|
|
|
#ifdef UNIX
|
|
//Hey Guys : IE4.01 has an error - it returns the wrong VTABLE
|
|
//when this QI is done. We know how our VTABLEs are laid out
|
|
|
|
#else
|
|
// IE4.01 has an error - it returns the wrong VTABLE
|
|
// when this QI is done.
|
|
|
|
if((LPVOID)m_pTask == (LPVOID)m_pExtract)
|
|
{
|
|
m_pTask = m_pTask + 2; // This vtable is two ptrs away and is in fstree.cpp in shell32 in IE4.01
|
|
}
|
|
#endif
|
|
|
|
if ( m_lState == IRTIR_TASK_RUNNING )
|
|
{
|
|
// start the extractor....
|
|
hr = m_pExtract->Extract( &m_hBmp );
|
|
}
|
|
|
|
if (( SUCCEEDED( hr ) || ( hr != E_PENDING && (m_dwFlags & EITF_ALWAYSCALL))) && m_lState == IRTIR_TASK_RUNNING )
|
|
{
|
|
hr = InternalResume();
|
|
}
|
|
|
|
if ( m_lState != IRTIR_TASK_SUSPENDED || hr != E_PENDING )
|
|
{
|
|
m_lState = IRTIR_TASK_FINISHED;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP CExtractImageTask::Kill ( BOOL fWait )
|
|
{
|
|
if ( m_lState != IRTIR_TASK_RUNNING )
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
LONG lRes = InterlockedExchange( &m_lState, IRTIR_TASK_PENDING );
|
|
if ( lRes == IRTIR_TASK_FINISHED )
|
|
{
|
|
m_lState = lRes;
|
|
return NOERROR;
|
|
}
|
|
|
|
// does it support IRunnableTask ? Can we kill it ?
|
|
HRESULT hr = E_NOTIMPL;
|
|
if ( m_pTask != NULL )
|
|
{
|
|
hr = m_pTask->Kill( FALSE );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP CExtractImageTask::Suspend( void )
|
|
{
|
|
if ( !m_pTask )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
if ( m_lState != IRTIR_TASK_RUNNING )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
LONG lRes = InterlockedExchange( &m_lState, IRTIR_TASK_SUSPENDED );
|
|
HRESULT hr = m_pTask->Suspend();
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
lRes = (LONG) m_pTask->IsRunning();
|
|
if ( lRes == IRTIR_TASK_SUSPENDED )
|
|
{
|
|
m_lState = lRes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_lState = lRes;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP CExtractImageTask::Resume( void )
|
|
{
|
|
if ( !m_pTask )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
if ( m_lState != IRTIR_TASK_SUSPENDED )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
m_lState = IRTIR_TASK_RUNNING;
|
|
|
|
HRESULT hr = m_pTask->Resume();
|
|
if ( SUCCEEDED( hr ) || ( hr != E_PENDING && ( m_dwFlags & EITF_ALWAYSCALL )))
|
|
{
|
|
hr = InternalResume();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT CExtractImageTask::InternalResume()
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
if ( m_dwFlags & EITF_ALWAYSCALL || m_hBmp )
|
|
{
|
|
// call the update function
|
|
hr = m_plb->UpdateLogoCallback( m_dwItem, m_iIcon, m_hBmp, m_szPath, TRUE );
|
|
}
|
|
|
|
m_lState = IRTIR_TASK_FINISHED;
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP_( ULONG ) CExtractImageTask:: IsRunning ( void )
|
|
{
|
|
return m_lState;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////CLogoBase/////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// static data...
|
|
IImageCache * CLogoBase::s_pSharedWideLogoCache = NULL;
|
|
long CLogoBase::s_lSharedWideLogosRef = 0;
|
|
HDSA CLogoBase::s_hdsaWideLogoIndices = NULL;
|
|
CRITICAL_SECTION CLogoBase::s_csSharedLogos = {0};
|
|
|
|
extern "C" void CLogoBase_Initialize( void )
|
|
{
|
|
CLogoBase::_Initialize();
|
|
}
|
|
|
|
extern "C" void CLogoBase_Cleanup( void )
|
|
{
|
|
CLogoBase::_Cleanup( );
|
|
}
|
|
|
|
void CLogoBase::_Initialize( void )
|
|
{
|
|
InitializeCriticalSection( &s_csSharedLogos );
|
|
}
|
|
|
|
void CLogoBase::_Cleanup( void )
|
|
{
|
|
DeleteCriticalSection( & s_csSharedLogos );
|
|
}
|
|
|
|
|
|
CLogoBase::CLogoBase( BOOL fWide )
|
|
{
|
|
// are we paletized, then use the global halftone palette ....
|
|
HDC hdcTmp = GetDC( NULL );
|
|
if (hdcTmp)
|
|
{
|
|
if (GetDeviceCaps( hdcTmp, RASTERCAPS) & RC_PALETTE)
|
|
{
|
|
ASSERT( g_hpalHalftone );
|
|
_hpalHalftone = g_hpalHalftone;
|
|
}
|
|
ReleaseDC( NULL, hdcTmp );
|
|
}
|
|
|
|
_fWide = fWide;
|
|
}
|
|
|
|
CLogoBase::~CLogoBase()
|
|
{
|
|
if (_pLogoCache || _pTaskScheduler)
|
|
{
|
|
ExitLogoView();
|
|
}
|
|
|
|
// NOTE: no palette release because we are using the global Halftone palette......
|
|
}
|
|
|
|
HRESULT CLogoBase::AddRefLogoCache( void )
|
|
{
|
|
if ( _fWide )
|
|
{
|
|
EnterCriticalSection( &s_csSharedLogos );
|
|
|
|
if ( !s_lSharedWideLogosRef )
|
|
{
|
|
if ( !s_hdsaWideLogoIndices )
|
|
{
|
|
s_hdsaWideLogoIndices = DSA_Create( sizeof( LogoIndex ), 5 );
|
|
if ( !s_hdsaWideLogoIndices )
|
|
{
|
|
LeaveCriticalSection( &s_csSharedLogos );
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
ASSERT( s_hdsaWideLogoIndices );
|
|
ASSERT( !s_pSharedWideLogoCache );
|
|
|
|
// BUGBUG for now CoCreate one per view
|
|
HRESULT hr = CoCreateInstance( CLSID_ImageListCache,
|
|
NULL,
|
|
CLSCTX_INPROC,
|
|
IID_IImageCache,
|
|
(void **) & s_pSharedWideLogoCache );
|
|
if ( FAILED( hr ))
|
|
{
|
|
LeaveCriticalSection( &s_csSharedLogos );
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
ASSERT( s_pSharedWideLogoCache );
|
|
|
|
// bump up the ref and get a pointer to it...
|
|
s_lSharedWideLogosRef ++;
|
|
_pLogoCache = s_pSharedWideLogoCache;
|
|
_pLogoCache->AddRef();
|
|
_hdsaLogoIndices = s_hdsaWideLogoIndices;
|
|
LeaveCriticalSection( &s_csSharedLogos );
|
|
|
|
return NOERROR;
|
|
}
|
|
else
|
|
{
|
|
// non wide logo version we don't share because w eonly expect there ever to be one...
|
|
_hdsaLogoIndices = DSA_Create( sizeof( LogoIndex ), 5 );
|
|
if ( !_hdsaLogoIndices )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// BUGBUG for now CoCreate one per view
|
|
return CoCreateInstance( CLSID_ImageListCache,
|
|
NULL,
|
|
CLSCTX_INPROC,
|
|
IID_IImageCache,
|
|
(void **) & _pLogoCache );
|
|
}
|
|
}
|
|
|
|
HRESULT CLogoBase::ReleaseLogoCache( void )
|
|
{
|
|
if ( !_pLogoCache )
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
ATOMICRELEASE(_pLogoCache);
|
|
|
|
if ( _fWide )
|
|
{
|
|
EnterCriticalSection( &s_csSharedLogos );
|
|
|
|
ASSERT( s_lSharedWideLogosRef > 0 );
|
|
|
|
s_lSharedWideLogosRef --;
|
|
if ( ! s_lSharedWideLogosRef )
|
|
{
|
|
// let go of the final ref.....
|
|
ATOMICRELEASE(s_pSharedWideLogoCache);
|
|
|
|
ASSERT( s_hdsaWideLogoIndices );
|
|
DSA_Destroy( s_hdsaWideLogoIndices );
|
|
s_hdsaWideLogoIndices = NULL;
|
|
}
|
|
|
|
LeaveCriticalSection( &s_csSharedLogos );
|
|
}
|
|
else
|
|
{
|
|
// free the HDSA
|
|
DSA_Destroy( _hdsaLogoIndices );
|
|
_hdsaLogoIndices = NULL;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT CLogoBase::InitLogoView( void )
|
|
{
|
|
HRESULT hr = AddRefLogoCache();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CoCreateInstance(CLSID_ShellTaskScheduler,
|
|
NULL,
|
|
CLSCTX_INPROC,
|
|
IID_IShellTaskScheduler,
|
|
(void **) &_pTaskScheduler);
|
|
if (FAILED(hr))
|
|
{
|
|
ATOMICRELEASE(_pLogoCache);
|
|
}
|
|
else
|
|
{
|
|
_rgLogoSize.cx = ( _fWide ) ? LOGO_WIDE_WIDTH : LOGO_WIDTH ;
|
|
_rgLogoSize.cy = LOGO_HEIGHT;
|
|
|
|
IMAGECACHEINITINFO rgInfo;
|
|
rgInfo.cbSize = sizeof( rgInfo );
|
|
rgInfo.dwMask = ICIIFLAG_LARGE;
|
|
rgInfo.iStart = 0;
|
|
rgInfo.iGrow = 5;
|
|
|
|
// the color depth is currently the screen resolution...
|
|
int iColorRes = SHGetCurColorRes();
|
|
|
|
_dwClrDepth = (DWORD) iColorRes;
|
|
switch (iColorRes)
|
|
{
|
|
case 16 : rgInfo.dwFlags = ILC_COLOR16;
|
|
break;
|
|
case 24 :
|
|
case 32 : rgInfo.dwFlags = ILC_COLOR24;
|
|
break;
|
|
default : rgInfo.dwFlags = ILC_COLOR8;
|
|
}
|
|
|
|
rgInfo.rgSizeLarge = _rgLogoSize;
|
|
if (_pLogoCache)
|
|
hr = _pLogoCache->GetImageList(&rgInfo);
|
|
else
|
|
hr = E_UNEXPECTED;
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
ATOMICRELEASE(_pLogoCache);
|
|
ATOMICRELEASE(_pTaskScheduler);
|
|
}
|
|
else
|
|
{
|
|
_himlLogos = rgInfo.himlLarge;
|
|
|
|
// GetImageList() will return S_FALSE if it was already created...
|
|
if ((hr == S_OK) && (iColorRes <= 8))
|
|
{
|
|
// init the color table so that it matches The "special halftone palette"
|
|
HPALETTE hpal = SHCreateShellPalette(NULL);
|
|
PALETTEENTRY rgColours[256];
|
|
RGBQUAD rgDIBColours[256];
|
|
|
|
ASSERT( hpal );
|
|
int nColours = GetPaletteEntries(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;
|
|
}
|
|
|
|
DeletePalette(hpal);
|
|
|
|
ImageList_SetColorTable(_himlLogos, 0, 256, rgDIBColours);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CLogoBase::ExitLogoView( void )
|
|
{
|
|
ATOMICRELEASE( _pTaskScheduler );
|
|
|
|
// the task scheduler callbacks can reference
|
|
// the logocache, so make sure you free the
|
|
// logo cache AFTER the task scheduler!
|
|
ReleaseLogoCache();
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
int CLogoBase::GetCachedLogoIndex( DWORD dwItem, LPCITEMIDLIST pidl, LPRUNNABLETASK *ppTask, DWORD * pdwPriority, DWORD *pdwFlags )
|
|
{
|
|
DWORD dwPassedFlags = 0;
|
|
|
|
if ( pdwFlags )
|
|
{
|
|
dwPassedFlags = *pdwFlags;
|
|
*pdwFlags = 0;
|
|
}
|
|
|
|
// No logo cache?
|
|
if (!_pLogoCache)
|
|
return 0;
|
|
|
|
ASSERT( pidl );
|
|
// HACK: this is used on browser only mode to tell what sort of logos we need...
|
|
UINT rgfFlags = _fWide;
|
|
LPEXTRACTIMAGE pImage = NULL;
|
|
int iImage = -1;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// IID_IEXtractLogo and IID_IExtractImage are the same interface, by using a new guid
|
|
// it means we can selectively decided what can logo in logo view...
|
|
hr = FakeGetUIObjectOf( GetSF(), pidl, &rgfFlags, IID_IExtractLogo, (void **) &pImage );
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
// extract ....
|
|
HBITMAP hImage;
|
|
WCHAR szPath[MAX_PATH];
|
|
DWORD dwFlags = IEIFLAG_ASYNC | IEIFLAG_ASPECT | dwPassedFlags;
|
|
IMAGECACHEINFO rgInfo;
|
|
UINT uIndex;
|
|
BOOL fAsync;
|
|
DWORD dwPriority;
|
|
|
|
rgInfo.cbSize = sizeof( rgInfo );
|
|
|
|
hr = pImage->GetLocation( szPath, MAX_PATH, &dwPriority, &_rgLogoSize, _dwClrDepth, &dwFlags );
|
|
fAsync = ( hr == E_PENDING );
|
|
if ( SUCCEEDED( hr ) || fAsync )
|
|
{
|
|
// mask off the flags passed to use by the flags returned from the extractor...
|
|
if ( pdwFlags )
|
|
*pdwFlags = dwPassedFlags & dwFlags;
|
|
|
|
rgInfo.dwMask = ICIFLAG_NAME;
|
|
rgInfo.pszName = szPath;
|
|
|
|
hr = _pLogoCache->FindImage( &rgInfo, &uIndex );
|
|
if ( hr == S_OK )
|
|
{
|
|
ATOMICRELEASE( pImage );
|
|
return (int) uIndex;
|
|
}
|
|
|
|
if ( fAsync )
|
|
{
|
|
LPRUNNABLETASK pTaskTmp = NULL;
|
|
|
|
ASSERT( _pTaskScheduler );
|
|
|
|
// pass the icon index so we can find the right logo later...
|
|
int iIcon = SHMapPIDLToSystemImageListIndex(GetSF(), pidl, NULL);
|
|
hr = CExtractImageTask_Create( this,
|
|
pImage,
|
|
szPath,
|
|
dwItem,
|
|
iIcon,
|
|
0,
|
|
&pTaskTmp );
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
if ( !ppTask )
|
|
{
|
|
hr = AddTaskToQueue( pTaskTmp, dwPriority, dwItem );
|
|
pTaskTmp->Release();
|
|
}
|
|
else
|
|
{
|
|
* ppTask = pTaskTmp;
|
|
|
|
ASSERT( pdwPriority );
|
|
*pdwPriority = dwPriority;
|
|
}
|
|
}
|
|
else if ( ppTask )
|
|
{
|
|
*ppTask = NULL;
|
|
}
|
|
|
|
// if all this failed, then we will just end up with a default
|
|
// logo. This is only likely to fail in low memory conditions,
|
|
// so that will be fine.
|
|
|
|
// if this SUCCEEDED we will drop through to pick up a defualt piccy for now.
|
|
}
|
|
else
|
|
{
|
|
// otherwise extract synchronously.......
|
|
hr = pImage->Extract( &hImage );
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
rgInfo.dwMask = ICIFLAG_NAME | ICIFLAG_LARGE | ICIFLAG_BITMAP | ICIFLAG_NOUSAGE;
|
|
rgInfo.hBitmapLarge = hImage;
|
|
|
|
hr = _pLogoCache->AddImage( &rgInfo, &uIndex );
|
|
DeleteObject( hImage );
|
|
}
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
iImage = (int ) uIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ATOMICRELEASE( pImage );
|
|
|
|
return iImage;
|
|
}
|
|
|
|
int CLogoBase::GetLogoIndex( DWORD dwItem, LPCITEMIDLIST pidl, LPRUNNABLETASK *ppTask, DWORD * pdwPriority, DWORD *pdwFlags )
|
|
{
|
|
int iImage = GetCachedLogoIndex(dwItem, pidl, ppTask, pdwPriority, pdwFlags );
|
|
|
|
if ( iImage == -1 )
|
|
{
|
|
// always pass FALSE, we want the proper ICON, cdfview no longer hits the
|
|
// wire for the icon so we can safely ask for the correct icon.
|
|
iImage = GetDefaultLogo( pidl, FALSE);
|
|
|
|
}
|
|
return iImage;
|
|
}
|
|
|
|
HRESULT CLogoBase::AddTaskToQueue( LPRUNNABLETASK pTask, DWORD dwPriority, DWORD dwItem )
|
|
{
|
|
ASSERT( _pTaskScheduler );
|
|
return _pTaskScheduler->AddTask( pTask, GetTOID(), dwItem, dwPriority );
|
|
}
|
|
|
|
int CLogoBase::GetDefaultLogo( LPCITEMIDLIST pidl, BOOL fQuick )
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
// Get icon to draw from
|
|
int iIndex = -1;
|
|
if ( !fQuick )
|
|
{
|
|
iIndex = SHMapPIDLToSystemImageListIndex(GetSF(), pidl, NULL);
|
|
}
|
|
if (iIndex < 0)
|
|
{
|
|
iIndex = II_DOCNOASSOC;
|
|
}
|
|
|
|
WCHAR wszText[MAX_PATH];
|
|
|
|
wszText[0] = 0;
|
|
|
|
STRRET strret;
|
|
HRESULT hr = GetSF()->GetDisplayNameOf( pidl, SHGDN_NORMAL, &strret );
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
StrRetToBufW(&strret, pidl, wszText, ARRAYSIZE(wszText));
|
|
}
|
|
|
|
UINT uCacheIndex = (UINT) -1;
|
|
|
|
if (_pLogoCache) // We didn't have one in stress.
|
|
{
|
|
IMAGECACHEINFO rgInfo;
|
|
rgInfo.cbSize = sizeof( rgInfo );
|
|
rgInfo.dwMask = ICIFLAG_NAME | ICIFLAG_INDEX;
|
|
rgInfo.pszName = wszText;
|
|
rgInfo.iIndex = iIndex;
|
|
|
|
hr = _pLogoCache->FindImage( &rgInfo, &uCacheIndex );
|
|
if ( hr == S_OK )
|
|
{
|
|
return uCacheIndex;
|
|
}
|
|
|
|
HBITMAP hDef;
|
|
hr = CreateDefaultLogo( iIndex, _rgLogoSize.cx, _rgLogoSize.cy, W2T(wszText), &hDef );
|
|
if ( SUCCEEDED( hr ))
|
|
{
|
|
rgInfo.hBitmapLarge = hDef;
|
|
rgInfo.hMaskLarge = NULL;
|
|
rgInfo.dwMask = ICIFLAG_NAME | ICIFLAG_INDEX | ICIFLAG_BITMAP | ICIFLAG_LARGE;
|
|
|
|
hr = _pLogoCache->AddImage( &rgInfo, &uCacheIndex );
|
|
if ( FAILED(hr ))
|
|
{
|
|
uCacheIndex = (UINT) -1;
|
|
}
|
|
else
|
|
{
|
|
// remember the index of the logo
|
|
AddIndicesToLogoList( iIndex, uCacheIndex );
|
|
}
|
|
DeleteObject( hDef );
|
|
}
|
|
}
|
|
|
|
return (int) uCacheIndex;
|
|
}
|
|
|
|
#define DXFUDGE 4
|
|
#define COLORTEXT RGB(255,255,255)
|
|
#define COLORBK RGB(0,0,0)
|
|
HRESULT CLogoBase::CreateDefaultLogo(int iIcon, int cxLogo, int cyLogo, LPCTSTR pszText, HBITMAP * phBmpLogo)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
HBITMAP hbmp = NULL;
|
|
|
|
HIMAGELIST himl;
|
|
int cxIcon, cyIcon;
|
|
int x, y, dx, dy;
|
|
|
|
// get the small icons....
|
|
Shell_GetImageLists(NULL, &himl);
|
|
ImageList_GetIconSize(himl, &cxIcon, &cyIcon);
|
|
|
|
// Calculate position info. We assume logos are wider than they are tall.
|
|
//
|
|
ASSERT(cxLogo >= cyLogo);
|
|
|
|
// Put the icon on the left
|
|
x = 2;
|
|
|
|
// Center the icon vertically
|
|
if (cyIcon <= cyLogo)
|
|
{
|
|
y = (cyLogo - cyIcon) / 2;
|
|
dy = cyIcon;
|
|
dx = cxIcon;
|
|
}
|
|
else
|
|
{
|
|
y = 0;
|
|
dy = cyLogo;
|
|
|
|
// keep shrinkage proportional
|
|
dx = MulDiv(cxIcon, cyIcon, cyLogo);
|
|
}
|
|
|
|
// get ready to draw
|
|
HDC hTBDC = GetDC( GetHWND());
|
|
if ( !hTBDC )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
HDC hdc = CreateCompatibleDC( hTBDC );
|
|
if (hdc)
|
|
{
|
|
RECT rc;
|
|
int dx, dy, x, y;
|
|
SIZE size;
|
|
hbmp = CreateCompatibleBitmap(hTBDC, cxLogo, cyLogo);
|
|
if (hbmp)
|
|
{
|
|
HGDIOBJ hTmp = SelectObject(hdc, hbmp);
|
|
HPALETTE hpalOld;
|
|
HFONT hfont, hfontOld;
|
|
|
|
if ( _hpalHalftone )
|
|
{
|
|
hpalOld = SelectPalette( hdc, _hpalHalftone, TRUE );
|
|
// LINTASSERT(hpalOld || !hpalOld); // 0 semi-ok for SelectPalette
|
|
RealizePalette( hdc );
|
|
}
|
|
|
|
SetMapMode( hdc, MM_TEXT );
|
|
rc.left = rc.top = 0;
|
|
rc.bottom = cyLogo;
|
|
rc.right = cxLogo;
|
|
SHFillRectClr(hdc, &rc, COLORBK);
|
|
// draw the icon into the memory DC.
|
|
ImageList_GetIconSize(himl, &dx, &dy);
|
|
x = DXFUDGE;
|
|
y = ((cyLogo- dy) >> 1);
|
|
ImageList_Draw( himl, iIcon, hdc, x, y, ILD_TRANSPARENT );
|
|
hfont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
|
hfontOld = hfont ? (HFONT)SelectObject(hdc, hfont) : NULL;
|
|
GetTextExtentPoint32(hdc, pszText, lstrlen(pszText), &size);
|
|
x += (dx + DXFUDGE);
|
|
y = ((cyLogo- size.cy) >> 1);
|
|
rc.left = x;
|
|
UINT eto = ETO_CLIPPED;
|
|
SetTextColor(hdc, COLORTEXT);
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
ExtTextOut(hdc, x, y, eto, &rc
|
|
, pszText, lstrlen(pszText), NULL);
|
|
if (hfontOld)
|
|
SelectObject(hdc, hfontOld);
|
|
|
|
if (hfont)
|
|
DeleteObject(hfont);
|
|
|
|
if ( _hpalHalftone )
|
|
{
|
|
(void) SelectPalette( hdc, hpalOld, TRUE );
|
|
RealizePalette( hdc );
|
|
}
|
|
|
|
// remove the final bitmap
|
|
SelectObject( hdc, hTmp );
|
|
hr = S_OK;
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DeleteObject(hbmp);
|
|
hbmp = NULL;
|
|
}
|
|
}
|
|
|
|
DeleteDC(hdc);
|
|
}
|
|
ReleaseDC( GetHWND(), hTBDC );
|
|
|
|
*phBmpLogo = hbmp;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CLogoBase::FlushLogoCache( )
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (_pLogoCache)
|
|
{
|
|
// forcibly clear out the logo cache so the items get refetched ...
|
|
_pLogoCache->Flush(TRUE);
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CLogoBase::DitherBitmap( HBITMAP hBmp, HBITMAP * phBmpNew )
|
|
{
|
|
// if ( !phBmpNew )
|
|
// {
|
|
// return E_INVALIDARG;
|
|
// }
|
|
//
|
|
// if ( _dwClrDepth > 8)
|
|
// {
|
|
// *phBmpNew = hBmp;
|
|
// return S_FALSE;
|
|
// }
|
|
//
|
|
// IIntDitherer * pDither;
|
|
// HRESULT hr = CoCreateInstance( CLSID_IntDitherer,
|
|
// NULL,
|
|
// CLSCTX_INPROC_SERVER,
|
|
// IID_IIntDitherer,
|
|
// (void **) & pDither );
|
|
// if ( FAILED( hr ))
|
|
// {
|
|
// return hr;
|
|
// }
|
|
//
|
|
// static BYTE rgb[32768];
|
|
// static BOOL fInit = FALSE;
|
|
//
|
|
// if ( !fInit )
|
|
// {
|
|
// // init the inverse color map table
|
|
// SHGetInverseCMAP( rgb, sizeof( rgb ));
|
|
// fInit = TRUE;
|
|
// }
|
|
//
|
|
// HDC hMemDc = CreateCompatibleDC( NULL );
|
|
// if ( !hMemDc )
|
|
// {
|
|
// pDither->Release();
|
|
// return E_FAIL;
|
|
// }
|
|
//
|
|
// HBITMAP hOld = SelectObject( hdc, hBmp );
|
|
//
|
|
// BITMAPINFO bi;
|
|
//
|
|
// ZeroMemory( &bi, sizeof( bi ));
|
|
// bi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
|
|
// bi.bmiHeader.biBitCount = 0;
|
|
// bi.bmiHeader.biCompression = 0;
|
|
//
|
|
// // get the header information....
|
|
// iRet = GetDIBits( hMemDc, hBmp, 0, 0, NULL, &bi, DIB_RGB_COLORS );
|
|
// if ( iRet != 0 )
|
|
// {
|
|
// LPVOID pBuffer, pBits;
|
|
// int iOffset = 0;
|
|
//
|
|
// if ( bi.bmiHeader.biCompression == BI_BITFIELDS )
|
|
// {
|
|
// iOffset = sizeof( DWORD ) * 3;
|
|
// }
|
|
// else if ( bi.bmiHeader.biBitCount <= 8 )
|
|
// {
|
|
// if ( bi.bmiHeader.biClrUsed )
|
|
// {
|
|
// iOffset = sizeof( RGBQUAD ) * bi.bmiHeader.biClrUsed;
|
|
// }
|
|
// else
|
|
// {
|
|
// iOffset = (1 << bi.bmiHeader.biBitCount) * sizeof( RGBQUAD );
|
|
// }
|
|
// }
|
|
//
|
|
// bi.bmiHeader.biHeight = iHeight;
|
|
//
|
|
// // calc
|
|
// pBuffer = LocalAlloc( LPTR, sizeof( BITMAPINFOHEADER ) +
|
|
// bi.bmiHeader.biSizeImage +
|
|
// iOffset );
|
|
//
|
|
// // calc the size of the colour table so we put the data afterwards...
|
|
// pBits = (( LPBYTE )pBuffer ) + sizeof( BITMAPINFOHEADER ) + iOffset;
|
|
//
|
|
// CopyMemory( pBuffer, &bi, sizeof( BITMAPINFOHEADER ) );
|
|
// iRet = GetDIBits( hMemDc, hBmp, 0, iHeight, pBits,
|
|
// ( LPBITMAPINFO )pBuffer, DIB_RGB_COLORS );
|
|
//
|
|
//
|
|
// // we know we are going to 256 colour bitmap, so create a DIBSECTION as the destination ...
|
|
// pDither->DitherTo8bpp( BYTE * pDestBits, LONG nDestPitch,
|
|
// BYTE * pSrcBits, LONG nSrcPitch, REFGUID bfidSrc,
|
|
// RGBQUAD * prgbDestColors, RGBQUAD * prgbSrcColors,
|
|
// rgb,
|
|
// LONG x, LONG y, LONG cx, LONG cy,
|
|
// -1, -1);
|
|
// }
|
|
// pDither->Release();
|
|
|
|
ASSERT( FALSE );
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
int CLogoBase::AddIndicesToLogoList( int iIcon, UINT uIndex )
|
|
{
|
|
int iRet = -1;
|
|
|
|
LogoIndex * pIndex;
|
|
LogoIndex rgNew;
|
|
|
|
rgNew.iIcon = iIcon;
|
|
rgNew.iLogo = (int) uIndex;
|
|
|
|
if ( _fWide )
|
|
{
|
|
EnterCriticalSection( &s_csSharedLogos );
|
|
}
|
|
|
|
// scan to see if we have an extact match already in there...
|
|
for ( int n = 0; n < DSA_GetItemCount( _hdsaLogoIndices ); n ++ )
|
|
{
|
|
pIndex = (LogoIndex *) DSA_GetItemPtr( _hdsaLogoIndices, n );
|
|
ASSERT( pIndex );
|
|
if ( pIndex->iLogo == (int) uIndex )
|
|
{
|
|
// set the icon just incase it changed...
|
|
pIndex->iIcon = iIcon;
|
|
iRet = n;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( iRet == -1 )
|
|
{
|
|
iRet = DSA_AppendItem( _hdsaLogoIndices, &rgNew );
|
|
}
|
|
|
|
if ( _fWide )
|
|
{
|
|
LeaveCriticalSection( &s_csSharedLogos );
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
int CLogoBase::FindLogoFromIcon( int iIcon, int * piLastLogo )
|
|
{
|
|
int iRet = -1;
|
|
|
|
if ( !piLastLogo )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
LogoIndex * pIndex;
|
|
|
|
if ( _fWide )
|
|
{
|
|
EnterCriticalSection( &s_csSharedLogos );
|
|
}
|
|
|
|
for ( int n = *piLastLogo + 1; n < DSA_GetItemCount( _hdsaLogoIndices ); n ++ )
|
|
{
|
|
pIndex = (LogoIndex *) DSA_GetItemPtr( _hdsaLogoIndices, n );
|
|
ASSERT( pIndex );
|
|
|
|
if ( pIndex->iIcon == iIcon )
|
|
{
|
|
*piLastLogo = n;
|
|
iRet = pIndex->iLogo;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( _fWide )
|
|
{
|
|
LeaveCriticalSection( &s_csSharedLogos );
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
HRESULT CISFBand_CreateEx(IShellFolder* psf, LPCITEMIDLIST pidl, REFIID riid, void **ppv)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (psf || pidl)
|
|
{
|
|
IShellFolderBand *psfb;
|
|
hr = CoCreateInstance(CLSID_ISFBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellFolderBand, &psfb));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psfb->InitializeSFB(psf, pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psfb->QueryInterface(riid, ppv);
|
|
}
|
|
psfb->Release();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|