You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1410 lines
49 KiB
1410 lines
49 KiB
#include "priv.h"
|
|
#include "sccls.h"
|
|
#include "nscband.h"
|
|
#include "nsc.h"
|
|
#include "resource.h"
|
|
#include "dhuihand.h"
|
|
#include <varutil.h>
|
|
|
|
#include <mluisupp.h>
|
|
|
|
#define DM_HISTBAND 0x0000000
|
|
#define DM_GUIPAINS 0x40000000
|
|
|
|
#define REGKEY_HISTORY_VIEW TEXT("HistoryViewType")
|
|
#define REGKEY_DEFAULT_SIZE 0x10
|
|
|
|
#define VIEWTYPE_MAX 0x4 // A "guess" at how many viewtypes thare will be
|
|
#define VIEWTYPE_REALLOC 0x4 // How many to realloc at a time
|
|
|
|
// these are temporary
|
|
#define MENUID_SEARCH 0x4e4e
|
|
|
|
// Distance between history search go and stop buttons
|
|
#define HISTSRCH_BUTTONDIST 6
|
|
|
|
extern HINSTANCE g_hinst;
|
|
|
|
#define WM_SEARCH_STATE (WM_USER + 314)
|
|
|
|
class CHistBand : public CNSCBand,
|
|
public IShellFolderSearchableCallback
|
|
{
|
|
friend HRESULT CHistBand_CreateInstance(IUnknown *punkOuter,
|
|
IUnknown **ppunk, LPCOBJECTINFO poi);
|
|
public:
|
|
// *** IUnknown methods ***
|
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
|
|
STDMETHODIMP_(ULONG) AddRef (void) { return CNSCBand::AddRef(); };
|
|
STDMETHODIMP_(ULONG) Release(void) { return CNSCBand::Release(); };
|
|
|
|
// *** IOleCommandTarget methods ***
|
|
STDMETHODIMP Exec(const GUID *pguidCmdGroup,
|
|
DWORD nCmdID,
|
|
DWORD nCmdexecopt,
|
|
VARIANTARG *pvarargIn,
|
|
VARIANTARG *pvarargOut);
|
|
|
|
// *** IOleWindow methods ***
|
|
// (overriding CNSCBand implementation
|
|
STDMETHODIMP GetWindow(HWND *phwnd);
|
|
|
|
// *** IInputObject methods ***
|
|
// (overriding CNSCBand/CToolBand's implementation)
|
|
STDMETHODIMP TranslateAcceleratorIO(LPMSG lpMsg);
|
|
|
|
// *** IDockingWindow methods ***
|
|
STDMETHODIMP ShowDW(BOOL fShow);
|
|
|
|
// *** IShellFolderSearchableCallback methods ***
|
|
STDMETHODIMP RunBegin(DWORD dwReserved);
|
|
STDMETHODIMP RunEnd(DWORD dwReserved);
|
|
|
|
protected:
|
|
virtual void _AddButtons(BOOL fAdd);
|
|
virtual HRESULT _OnRegisterBand(IOleCommandTarget *poctProxy);
|
|
virtual BOOL _ShouldNavigateToPidl(LPCITEMIDLIST pidl, ULONG ulAttrib);
|
|
virtual HRESULT _NavigateRightPane(IShellBrowser *psb, LPCITEMIDLIST pidl);
|
|
|
|
~CHistBand();
|
|
|
|
HRESULT _InitViewPopup();
|
|
HRESULT _DoViewPopup(int x, int y);
|
|
HRESULT _ViewPopupSelect(UINT idCmd);
|
|
|
|
#ifdef SPLIT_HISTORY_VIEW_BUTTON
|
|
UINT _NextMenuItem();
|
|
#endif
|
|
|
|
HRESULT _ChangePidl(LPITEMIDLIST);
|
|
HRESULT _SelectPidl(LPCITEMIDLIST pidlSelect, BOOL fCreate,
|
|
LPCITEMIDLIST pidlViewType = NULL,
|
|
BOOL fReinsert = FALSE);
|
|
|
|
virtual HRESULT _InitializeNsc();
|
|
LPITEMIDLIST _GetCurrentSelectPidl(IOleCommandTarget *poctProxy = NULL);
|
|
HRESULT _SetRegistryPersistView(int iMenuID);
|
|
int _GetRegistryPersistView();
|
|
LPCITEMIDLIST _MenuIDToPIDL(UINT uMenuID);
|
|
int _PIDLToMenuID(LPITEMIDLIST pidl);
|
|
IShellFolderViewType* _GetViewTypeInfo();
|
|
HRESULT _GetHistoryViews();
|
|
HRESULT _FreeViewInfo();
|
|
|
|
void _ResizeChildWindows(LONG width, LONG height, BOOL fRepaint);
|
|
HRESULT _DoSearchUIStuff();
|
|
HRESULT _ExecuteSearch(LPTSTR pszSearchString);
|
|
HRESULT _ClearSearch();
|
|
IShellFolderSearchable *_EnsureSearch();
|
|
static LRESULT CALLBACK s_EditWndSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
static LRESULT CALLBACK s_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
static BOOL_PTR CALLBACK s_HistSearchDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
BOOL _fStrsAdded; // Strings from resource have been added as buttons on the toolbar
|
|
LONG_PTR _lStrOffset;
|
|
|
|
HMENU _hViewMenu; // an instance var so we can cache it
|
|
UINT _uViewCheckedItem; // which menuitem in the View menu is checked?
|
|
|
|
LPITEMIDLIST *_ppidlViewTypes;
|
|
LPTSTR *_ppszStrViewNames;
|
|
UINT _nViews;
|
|
int _iMaxMenuID;
|
|
|
|
HWND _hwndNSC;
|
|
HWND _hwndSearchDlg;
|
|
LONG _lSearchDlgHeight;
|
|
LPITEMIDLIST _pidlSearch; // current search
|
|
IShellFolderSearchable *_psfSearch;
|
|
|
|
LPITEMIDLIST _pidlHistory; // cache the history pidl from SHGetHistoryPIDL
|
|
IShellFolder *_psfHistory; // cache the history shell folder
|
|
IShellFolderViewType *_psfvtCache; // view type information
|
|
|
|
LPITEMIDLIST _pidlLastSelect;
|
|
};
|
|
|
|
CHistBand::~CHistBand()
|
|
{
|
|
DestroyMenu(_hViewMenu);
|
|
if (_pidlLastSelect)
|
|
ILFree(_pidlLastSelect);
|
|
if (_pidlHistory)
|
|
ILFree(_pidlHistory);
|
|
if (_psfHistory)
|
|
_psfHistory->Release();
|
|
if (_psfvtCache)
|
|
_psfvtCache->Release();
|
|
|
|
_ClearSearch(); // Frees _pidlSearch
|
|
if (_psfSearch)
|
|
_psfSearch->Release();
|
|
|
|
_FreeViewInfo();
|
|
}
|
|
|
|
HRESULT CHistBand::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CHistBand, IShellFolderSearchableCallback), // IID_IShellFolderSearchableCallback
|
|
{ 0 },
|
|
};
|
|
HRESULT hr = QISearch(this, qit, riid, ppvObj);
|
|
if (FAILED(hr))
|
|
hr = CNSCBand::QueryInterface(riid, ppvObj);
|
|
return hr;
|
|
}
|
|
|
|
// *** IOleCommandTarget methods ***
|
|
HRESULT CHistBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
|
|
DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
|
|
{
|
|
HRESULT hRes = S_OK;
|
|
if (pguidCmdGroup)
|
|
{
|
|
if (IsEqualGUID(CLSID_HistBand, *pguidCmdGroup))
|
|
{
|
|
switch(nCmdID)
|
|
{
|
|
case FCIDM_HISTBAND_VIEW:
|
|
if (pvarargIn && (pvarargIn->vt == VT_I4))
|
|
{
|
|
#ifdef SPLIT_HISTORY_VIEW_BUTTON
|
|
if (nCmdexecopt == OLECMDEXECOPT_PROMPTUSER)
|
|
hRes = _DoViewPopup(GET_X_LPARAM(pvarargIn->lVal), GET_Y_LPARAM(pvarargIn->lVal));
|
|
else
|
|
hRes = _ViewPopupSelect(_NextMenuItem());
|
|
#else
|
|
ASSERT(nCmdexecopt == OLECMDEXECOPT_PROMPTUSER);
|
|
hRes = _DoViewPopup(GET_X_LPARAM(pvarargIn->lVal), GET_Y_LPARAM(pvarargIn->lVal));
|
|
#endif
|
|
}
|
|
else
|
|
ASSERT(0);
|
|
break;
|
|
|
|
case FCIDM_HISTBAND_SEARCH:
|
|
_ViewPopupSelect(MENUID_SEARCH);
|
|
break;
|
|
}
|
|
}
|
|
else if ((IsEqualGUID(CGID_Explorer, *pguidCmdGroup)))
|
|
{
|
|
switch (nCmdID)
|
|
{
|
|
case SBCMDID_SELECTHISTPIDL:
|
|
#ifdef ANNOYING_HISTORY_AUTOSELECT
|
|
if (_uViewCheckedItem != MENUID_SEARCH)
|
|
{
|
|
LPCITEMIDLIST pidlSelect = VariantToIDList(pvarargIn);
|
|
|
|
// Get the current view information
|
|
LPCITEMIDLIST pidlView = _MenuIDToPIDL(_uViewCheckedItem);
|
|
DWORD dwViewFlags = SFVTFLAG_NOTIFY_CREATE;
|
|
IShellFolderViewType* psfvtInfo = _GetViewTypeInfo();
|
|
|
|
if (psfvtInfo)
|
|
{
|
|
// query for view type properties -- this will tell us how to
|
|
// select the item...
|
|
hRes = psfvtInfo->GetViewTypeProperties(pidlView,
|
|
&dwViewFlags);
|
|
psfvtInfo->Release();
|
|
}
|
|
if (SUCCEEDED(hRes))
|
|
{
|
|
hRes = _SelectPidl(pidlSelect, dwViewFlags & SFVTFLAG_NOTIFY_CREATE,
|
|
pidlView, dwViewFlags & SFVTFLAG_NOTIFY_RESORT);
|
|
}
|
|
ILFree(pidlSelect);
|
|
}
|
|
else //eat it, so that nsc doesn't get it
|
|
hRes = S_OK;
|
|
#endif //ANNOYING_HISTORY_AUTOSELECT
|
|
hRes = S_OK;
|
|
break;
|
|
|
|
case SBCMDID_FILEDELETE:
|
|
hRes = _InvokeCommandOnItem(TEXT("delete"));
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
hRes = CNSCBand::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
|
|
}
|
|
else
|
|
hRes = CNSCBand::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
|
|
return hRes;
|
|
}
|
|
|
|
// *** IInputObject methods ***
|
|
HRESULT CHistBand::TranslateAcceleratorIO(LPMSG pmsg)
|
|
{
|
|
#ifdef DEBUG
|
|
if (pmsg->message == WM_KEYDOWN)
|
|
TraceMsg(DM_GUIPAINS, "CHistBand -- TranslateAcceleratorIO called and _hwndSearchDlg is %x", _hwndSearchDlg);
|
|
#endif
|
|
|
|
HWND hwndFocus = GetFocus();
|
|
|
|
// Translate accelerator messages for dialog
|
|
if ( (_hwndSearchDlg) && (hwndFocus != _hwndNSC) && (!hwndFocus || !IsChild(_hwndNSC, hwndFocus)) )
|
|
{
|
|
if (pmsg->message == WM_KEYDOWN)
|
|
{
|
|
if (IsVK_TABCycler(pmsg))
|
|
{
|
|
BOOL fBackwards = (GetAsyncKeyState(VK_SHIFT) < 0);
|
|
HWND hwndCur = pmsg->hwnd;
|
|
if (GetParent(pmsg->hwnd) != _hwndSearchDlg)
|
|
hwndCur = NULL;
|
|
|
|
HWND hwndNext = GetNextDlgTabItem(_hwndSearchDlg, hwndCur, fBackwards);
|
|
|
|
// Get the First dialog item in this searching order
|
|
HWND hwndFirst;
|
|
if (!fBackwards)
|
|
{
|
|
hwndFirst = GetNextDlgTabItem(_hwndSearchDlg, NULL, FALSE);
|
|
}
|
|
else
|
|
{
|
|
// passing NULL for the 2nd parameter returned NULL with ERROR_SUCCESS,
|
|
// so this is a workaround
|
|
hwndFirst = GetNextDlgTabItem(_hwndSearchDlg, GetNextDlgTabItem(_hwndSearchDlg, NULL, FALSE), TRUE);
|
|
}
|
|
|
|
// If the next dialog tabstop is the first dialog tabstop, then
|
|
// let someone else get focus
|
|
if ((!hwndCur) || (hwndNext != hwndFirst))
|
|
{
|
|
SetFocus(hwndNext);
|
|
return S_OK;
|
|
}
|
|
else if (!fBackwards)
|
|
{
|
|
SetFocus(_hwndNSC);
|
|
return S_OK;
|
|
}
|
|
}
|
|
else if ((pmsg->wParam == VK_RETURN))
|
|
SendMessage(_hwndSearchDlg, WM_COMMAND, MAKELONG(GetDlgCtrlID(pmsg->hwnd), 0), 0L);
|
|
}
|
|
// The History Search Edit Box is activated
|
|
if (pmsg->hwnd == GetDlgItem(_hwndSearchDlg, IDC_EDITHISTSEARCH))
|
|
{
|
|
// If the user pressed tab within the dialog
|
|
return EditBox_TranslateAcceleratorST(pmsg);
|
|
}
|
|
}
|
|
return CNSCBand::TranslateAcceleratorIO(pmsg);
|
|
}
|
|
|
|
// sends appropriate resize messages to our children windows
|
|
void CHistBand::_ResizeChildWindows(LONG width, LONG height, BOOL fRepaint)
|
|
{
|
|
if (_hwndNSC)
|
|
{
|
|
int y1 = _hwndSearchDlg ? _lSearchDlgHeight : 0;
|
|
int y2 = _hwndSearchDlg ? height - _lSearchDlgHeight : height;
|
|
|
|
MoveWindow(_hwndNSC, 0, y1, width, y2, fRepaint);
|
|
}
|
|
|
|
if (_hwndSearchDlg)
|
|
{
|
|
MoveWindow(_hwndSearchDlg, 0, 0, width, _lSearchDlgHeight, fRepaint);
|
|
}
|
|
}
|
|
|
|
HRESULT CHistBand::_DoSearchUIStuff()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// host the search dialog inside my window:
|
|
_hwndSearchDlg = CreateDialogParam(MLGetHinst(), MAKEINTRESOURCE(DLG_HISTSEARCH2),
|
|
_hwnd, s_HistSearchDlgProc, reinterpret_cast<LPARAM>(this));
|
|
|
|
if (_hwndSearchDlg)
|
|
{
|
|
RECT rcSelf;
|
|
GetClientRect(_hwnd, &rcSelf);
|
|
|
|
RECT rcDlg;
|
|
GetClientRect(_hwndSearchDlg, &rcDlg);
|
|
|
|
_lSearchDlgHeight = rcDlg.bottom;
|
|
|
|
_ResizeChildWindows(rcSelf.right, rcSelf.bottom, TRUE);
|
|
ShowWindow(_hwndSearchDlg, SW_SHOWDEFAULT);
|
|
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// WndProc for main window to go in rebar
|
|
LRESULT CALLBACK CHistBand::s_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CHistBand* phb = reinterpret_cast<CHistBand *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_SETFOCUS:
|
|
{
|
|
TraceMsg(DM_GUIPAINS, "Histband Parent -- SETFOCUS");
|
|
// The only way this should be called is via a RB_CYCLEFOCUS->...->UIActivateIO->SetFocus
|
|
// therefore, we can assume that we're being tabbed into or something with equally good.
|
|
// If we tab into the outer dummy window, transfer the focus to
|
|
// our appropriate child:
|
|
BOOL fBackwards = (GetAsyncKeyState(VK_SHIFT) < 0);
|
|
if (phb->_hwndSearchDlg) {
|
|
// Select either the first or the last item in the dialog depending on
|
|
// whether we're shifting in or shifting out
|
|
SetFocus(GetNextDlgTabItem(phb->_hwndSearchDlg, (NULL), fBackwards));
|
|
}
|
|
else {
|
|
TraceMsg(DM_GUIPAINS, "NSC is being given focus!");
|
|
SetFocus(phb->_hwndNSC);
|
|
}
|
|
}
|
|
return 0;
|
|
case WM_CREATE:
|
|
SetWindowLongPtr(hWnd, GWLP_USERDATA,
|
|
(reinterpret_cast<LONG_PTR>((reinterpret_cast<CREATESTRUCT *>(lParam))->lpCreateParams)));
|
|
return 0;
|
|
case WM_SIZE:
|
|
if (phb)
|
|
phb->_ResizeChildWindows(LOWORD(lParam), HIWORD(lParam), TRUE);
|
|
return 0;
|
|
case WM_NCDESTROY:
|
|
//make sure the search object gets freed when the view/window is destroyed, because it holds a ref to us
|
|
phb->_ClearSearch(); // should we null out GWLP_USERDATA?
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
if (phb)
|
|
{
|
|
// We proxy the notification messages to our own parent who thinks that we
|
|
// are the namespace control
|
|
LPNMHDR pnmh = (LPNMHDR)lParam;
|
|
|
|
// Notification message coming from NSC
|
|
if (pnmh->hwndFrom == phb->_hwndNSC)
|
|
return SendMessage(phb->_hwndParent, msg, wParam, lParam);
|
|
}
|
|
} // INTENTIONAL FALLTHROUGH
|
|
}
|
|
return DefWindowProc(hWnd, msg, wParam, lParam);
|
|
}
|
|
|
|
// *** IOleWindow methods ***
|
|
HRESULT CHistBand::GetWindow(HWND *phwnd)
|
|
{
|
|
if (!_hwnd)
|
|
{
|
|
// we want to wrap a window around the namespace control so
|
|
// that we can add siblings later
|
|
|
|
// Get our parent's dimensions
|
|
RECT rcParent;
|
|
GetClientRect(_hwndParent, &rcParent);
|
|
|
|
static LPTSTR pszClassName = TEXT("History Pane");
|
|
|
|
WNDCLASSEX wndclass = { 0 };
|
|
wndclass.cbSize = sizeof(wndclass);
|
|
wndclass.style = CS_PARENTDC | CS_HREDRAW | CS_VREDRAW;
|
|
wndclass.lpfnWndProc = s_WndProc;
|
|
wndclass.hInstance = g_hinst;
|
|
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wndclass.lpszClassName = pszClassName;
|
|
|
|
RegisterClassEx(&wndclass);
|
|
|
|
_hwnd = CreateWindow(pszClassName, TEXT("History Window"),
|
|
WS_CHILD | WS_TABSTOP,
|
|
0, 0, rcParent.right, rcParent.bottom,
|
|
_hwndParent, NULL, g_hinst, (LPVOID)this);
|
|
}
|
|
|
|
if (_hwnd) // Host NSC
|
|
_pns->CreateTree(_hwnd, _GetTVStyle(), &_hwndNSC);
|
|
|
|
return CToolBand::GetWindow(phwnd);
|
|
}
|
|
|
|
// *** IDockingWindow methods ***
|
|
HRESULT CHistBand::ShowDW(BOOL fShow)
|
|
{
|
|
HRESULT hr = CNSCBand::ShowDW(fShow);
|
|
_AddButtons(fShow);
|
|
return hr;
|
|
}
|
|
|
|
static const TBBUTTON c_tbHistory[] =
|
|
{
|
|
{ I_IMAGENONE, FCIDM_HISTBAND_VIEW, TBSTATE_ENABLED, BTNS_AUTOSIZE | BTNS_WHOLEDROPDOWN | BTNS_SHOWTEXT, {0,0}, 0, 0 },
|
|
{ 2, FCIDM_HISTBAND_SEARCH, TBSTATE_ENABLED, BTNS_AUTOSIZE | BTNS_SHOWTEXT, {0,0}, 0, 1 },
|
|
};
|
|
|
|
// Adds buttons from the above table to the Explorer
|
|
void CHistBand::_AddButtons(BOOL fAdd)
|
|
{
|
|
// don't add button if we have no menu
|
|
if (!_hViewMenu)
|
|
return;
|
|
|
|
IExplorerToolbar* piet;
|
|
if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IExplorerToolbar, &piet))))
|
|
{
|
|
if (fAdd)
|
|
{
|
|
piet->SetCommandTarget((IUnknown*)SAFECAST(this, IOleCommandTarget*), &CLSID_HistBand, 0);
|
|
|
|
if (!_fStrsAdded)
|
|
{
|
|
piet->AddString(&CLSID_HistBand, MLGetHinst(), IDS_HIST_BAR_LABELS, &_lStrOffset);
|
|
_fStrsAdded = TRUE;
|
|
}
|
|
|
|
_EnsureImageListsLoaded();
|
|
piet->SetImageList(&CLSID_HistBand, _himlNormal, _himlHot, NULL);
|
|
|
|
TBBUTTON tbHistory[ARRAYSIZE(c_tbHistory)];
|
|
memcpy(tbHistory, c_tbHistory, sizeof(TBBUTTON) * ARRAYSIZE(c_tbHistory));
|
|
for (int i = 0; i < ARRAYSIZE(c_tbHistory); i++)
|
|
tbHistory[i].iString += (long) _lStrOffset;
|
|
|
|
piet->AddButtons(&CLSID_HistBand, ARRAYSIZE(tbHistory), tbHistory);
|
|
}
|
|
else
|
|
piet->SetCommandTarget(NULL, NULL, 0);
|
|
|
|
piet->Release();
|
|
}
|
|
}
|
|
|
|
// *** IShellFolderSearchableCallback methods ***
|
|
// enable and disable cancel buttons
|
|
HRESULT CHistBand::RunBegin(DWORD dwReserved)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
if (_hwndSearchDlg)
|
|
{
|
|
SendMessage(_hwndSearchDlg, WM_SEARCH_STATE, (WPARAM)TRUE, NULL);
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CHistBand::RunEnd(DWORD dwReserved)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
if (_hwndSearchDlg)
|
|
{
|
|
SendMessage(_hwndSearchDlg, WM_SEARCH_STATE, (WPARAM)FALSE, NULL);
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// A utility function used in the WM_SIZE handling below...
|
|
inline HWND _GetHwndAndRect(HWND hwndDlg, int item, BOOL fClient, RECT &rc)
|
|
{
|
|
HWND hwnd = GetDlgItem(hwndDlg, item);
|
|
if (fClient)
|
|
{
|
|
GetClientRect(hwnd, &rc);
|
|
}
|
|
else
|
|
{
|
|
GetWindowRect(hwnd, &rc);
|
|
MapWindowPoints(NULL, hwndDlg, ((LPPOINT)&rc), 2);
|
|
}
|
|
return hwnd;
|
|
}
|
|
|
|
LRESULT CALLBACK CHistBand::s_EditWndSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
case WM_KEYDOWN:
|
|
if ((GetAsyncKeyState(VK_CONTROL) < 0) && (wParam == TEXT('U')))
|
|
{
|
|
uMsg = WM_SETTEXT;
|
|
wParam = 0;
|
|
lParam = ((LPARAM)(LPCTSTR)TEXT(""));
|
|
}
|
|
break;
|
|
|
|
case WM_CHAR:
|
|
if (wParam == VK_RETURN)
|
|
{
|
|
PostMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(IDB_HISTSRCH_GO, 0), 0L);
|
|
return 0L;
|
|
}
|
|
break;
|
|
}
|
|
return CallWindowProc((WNDPROC)(GetWindowLongPtr(hwnd, GWLP_USERDATA)), hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
|
|
// Please see note at top of file for explanation...
|
|
INT_PTR CALLBACK CHistBand::s_HistSearchDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
case WM_PAINT:
|
|
{
|
|
// paint a little separator bar on the bottom
|
|
PAINTSTRUCT ps;
|
|
RECT rcSelf;
|
|
HDC hdc = BeginPaint(hwndDlg, &ps);
|
|
GetClientRect(hwndDlg, &rcSelf);
|
|
RECT rcFill = { 0, rcSelf.bottom - 2, rcSelf.right, rcSelf.bottom };
|
|
FillRect(hdc, &rcFill, GetSysColorBrush(COLOR_BTNFACE));
|
|
EndPaint(hwndDlg, &ps);
|
|
break;
|
|
}
|
|
|
|
// Supply child controls with correct bkgd color
|
|
case WM_CTLCOLORSTATIC:
|
|
if ((HWND)lParam == GetDlgItem(hwndDlg, IDD_HISTSRCH_ANIMATION))
|
|
{
|
|
SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
|
|
return (INT_PTR) GetSysColorBrush(COLOR_WINDOW);
|
|
}
|
|
else
|
|
{
|
|
SetBkMode((HDC)wParam, TRANSPARENT);
|
|
return (INT_PTR) GetSysColorBrush(COLOR_WINDOW);
|
|
}
|
|
case WM_CTLCOLORDLG:
|
|
//SetBkColor((HDC)HIWORD(lParam), GetSysColor(COLOR_WINDOW));
|
|
return (INT_PTR) GetSysColorBrush(COLOR_WINDOW);
|
|
case WM_INITDIALOG:
|
|
{
|
|
HWND hwndEdit = GetDlgItem(hwndDlg, IDC_EDITHISTSEARCH);
|
|
WNDPROC pfnOldEditProc = (WNDPROC)(GetWindowLongPtr(hwndEdit, GWLP_WNDPROC));
|
|
|
|
// subclass the editbox
|
|
SetWindowLongPtr(hwndEdit, GWLP_USERDATA, (LPARAM)pfnOldEditProc);
|
|
SetWindowLongPtr(hwndEdit, GWLP_WNDPROC, (LPARAM)s_EditWndSubclassProc);
|
|
|
|
SetWindowLongPtr(hwndDlg, DWLP_USER, lParam);
|
|
Animate_Open(GetDlgItem(hwndDlg, IDD_HISTSRCH_ANIMATION),
|
|
MAKEINTRESOURCE(IDA_HISTSEARCHAVI));
|
|
|
|
// limit the edit control to MAX_PATH-1 characters
|
|
Edit_LimitText(hwndEdit, MAX_PATH-1);
|
|
|
|
break;
|
|
}
|
|
case WM_DESTROY:
|
|
Animate_Close(GetDlgItem(hwndDlg, IDD_HISTSRCH_ANIMATION));
|
|
break;
|
|
case WM_SIZE:
|
|
{
|
|
if (wParam == SIZE_RESTORED)
|
|
{
|
|
UINT uWidth = LOWORD(lParam);
|
|
UINT uHeight = HIWORD(lParam);
|
|
|
|
RECT rcAnimSize, rcCancel, rcSearch, rcEdit, rcStatic;
|
|
HWND hwndAnim = _GetHwndAndRect(hwndDlg, IDD_HISTSRCH_ANIMATION, TRUE, rcAnimSize);
|
|
HWND hwndCancel = _GetHwndAndRect(hwndDlg, IDCANCEL, FALSE, rcCancel);
|
|
HWND hwndSearch = _GetHwndAndRect(hwndDlg, IDB_HISTSRCH_GO, FALSE, rcSearch);
|
|
HWND hwndEdit = _GetHwndAndRect(hwndDlg, IDC_EDITHISTSEARCH, FALSE, rcEdit);
|
|
|
|
// calculate the minimum tolerable width
|
|
UINT uMinWidth = ((rcCancel.right - rcCancel.left) +
|
|
(rcSearch.right - rcSearch.left) + HISTSRCH_BUTTONDIST +
|
|
rcEdit.left +
|
|
rcAnimSize.right + 1);
|
|
|
|
if (uWidth < uMinWidth)
|
|
uWidth = uMinWidth;
|
|
|
|
HDWP hdwp = BeginDeferWindowPos(5);
|
|
if (hdwp)
|
|
{
|
|
// align the animation box with the upper-right corner
|
|
DeferWindowPos(hdwp, hwndAnim, HWND_TOP, uWidth - rcAnimSize.right, 0,
|
|
rcAnimSize.right, rcAnimSize.bottom, SWP_NOZORDER);
|
|
|
|
// stretch the textbox as wide as possible
|
|
UINT uNewTextWidth = uWidth - rcAnimSize.right - 1 - rcEdit.left;
|
|
DeferWindowPos(hdwp, hwndEdit, HWND_TOP, rcEdit.left, rcEdit.top, uNewTextWidth,
|
|
rcEdit.bottom - rcEdit.top, SWP_NOZORDER);
|
|
|
|
// static text should not be longer than edit textbox
|
|
HWND hwndStatic = _GetHwndAndRect(hwndDlg, IDC_HISTSRCH_STATIC, FALSE, rcStatic);
|
|
DeferWindowPos(hdwp, hwndStatic, HWND_TOP, rcEdit.left, rcStatic.top, uNewTextWidth,
|
|
rcStatic.bottom - rcStatic.top, SWP_NOZORDER);
|
|
|
|
// align the cancel button with the right of the edit box
|
|
UINT uCancelLeft = uWidth - rcAnimSize.right - 1 - (rcCancel.right - rcCancel.left);
|
|
DeferWindowPos(hdwp, hwndCancel, HWND_TOP, uCancelLeft, rcCancel.top,
|
|
rcCancel.right - rcCancel.left, rcCancel.bottom - rcCancel.top, SWP_NOZORDER);
|
|
|
|
// align the search button so that it ends six pixels (HISTSRCH_BUTTONDIST)
|
|
// to the left of the cancel button
|
|
DeferWindowPos(hdwp, hwndSearch, HWND_TOP,
|
|
uCancelLeft - HISTSRCH_BUTTONDIST - (rcSearch.right - rcSearch.left),
|
|
rcSearch.top, rcSearch.right - rcSearch.left, rcSearch.bottom - rcSearch.top, SWP_NOZORDER);
|
|
|
|
EndDeferWindowPos(hdwp);
|
|
}
|
|
}
|
|
else
|
|
return FALSE;
|
|
break;
|
|
}
|
|
case WM_COMMAND:
|
|
{
|
|
CHistBand *phb = reinterpret_cast<CHistBand *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
|
|
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDC_EDITHISTSEARCH:
|
|
switch (HIWORD(wParam))
|
|
{
|
|
case EN_SETFOCUS:
|
|
// This guy allows us to intercept TranslateAccelerator messages
|
|
// like backspace. This is the same as calling UIActivateIO(TRUE), but
|
|
// doesn't cause an infinite setfocus loop in Win95
|
|
IUnknown_OnFocusChangeIS(phb->_punkSite, SAFECAST(phb, IInputObject*), TRUE);
|
|
SetFocus((HWND)lParam);
|
|
break;
|
|
case EN_CHANGE:
|
|
// Enable 'Go Fish' button iff there is text in the edit box
|
|
EnableWindow(GetDlgItem(hwndDlg, IDB_HISTSRCH_GO),
|
|
(bool) SendDlgItemMessage(hwndDlg, IDC_EDITHISTSEARCH, EM_LINELENGTH, 0, 0));
|
|
break;
|
|
}
|
|
break;
|
|
case IDB_HISTSRCH_GO:
|
|
{
|
|
TCHAR szSearchString[MAX_PATH];
|
|
if (GetDlgItemText(hwndDlg, IDC_EDITHISTSEARCH, szSearchString, ARRAYSIZE(szSearchString)))
|
|
{
|
|
IServiceProvider *pServiceProvider;
|
|
|
|
HRESULT hr = IUnknown_QueryService(phb->_punkSite,
|
|
SID_SProxyBrowser,
|
|
IID_PPV_ARG(IServiceProvider,
|
|
&pServiceProvider));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IWebBrowser2 *pWebBrowser2;
|
|
hr = pServiceProvider->QueryService(SID_SWebBrowserApp,
|
|
IID_PPV_ARG(IWebBrowser2,
|
|
&pWebBrowser2));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
::PutFindText(pWebBrowser2, szSearchString);
|
|
pWebBrowser2->Release();
|
|
}
|
|
|
|
pServiceProvider->Release();
|
|
}
|
|
|
|
phb->_ExecuteSearch(szSearchString);
|
|
}
|
|
}
|
|
break;
|
|
case IDCANCEL:
|
|
{
|
|
if (phb->_EnsureSearch())
|
|
{
|
|
phb->_psfSearch->CancelAsyncSearch(phb->_pidlSearch, NULL);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
|
|
case WM_SEARCH_STATE:
|
|
{
|
|
BOOL fStart = (BOOL)wParam;
|
|
if (fStart)
|
|
{
|
|
Animate_Play(GetDlgItem(hwndDlg, IDD_HISTSRCH_ANIMATION), 0, -1, -1);
|
|
}
|
|
else
|
|
{
|
|
HWND hwndAnim = GetDlgItem(hwndDlg, IDD_HISTSRCH_ANIMATION);
|
|
Animate_Stop(hwndAnim);
|
|
Animate_Seek(hwndAnim, 0); // reset the animation
|
|
|
|
//HACK for IE5 ship
|
|
//if there's only one item found in history search, the item doesn't display
|
|
//because someone (comctl32?) set redraw to false.
|
|
//so, manually force it to true when the search stops
|
|
CHistBand *phb = reinterpret_cast<CHistBand *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
|
|
if (phb)
|
|
SendMessage(phb->_hwndNSC, WM_SETREDRAW, TRUE, 0);
|
|
}
|
|
HWND hwndFocus = GetFocus();
|
|
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_EDITHISTSEARCH), !fStart);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDB_HISTSRCH_GO), !fStart);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), fStart);
|
|
|
|
//make sure the focus goes to the right place
|
|
if ((NULL != hwndFocus) && (hwndFocus == GetDlgItem(hwndDlg, IDC_EDITHISTSEARCH) ||
|
|
(hwndFocus == GetDlgItem(hwndDlg, IDCANCEL))))
|
|
SetFocus(GetDlgItem(hwndDlg, fStart ? IDCANCEL : IDC_EDITHISTSEARCH));
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
IShellFolderSearchable *CHistBand::_EnsureSearch()
|
|
{
|
|
ASSERT(_psfHistory);
|
|
|
|
if (!_pidlSearch)
|
|
{
|
|
_psfHistory->QueryInterface(IID_PPV_ARG(IShellFolderSearchable, &_psfSearch));
|
|
}
|
|
return _psfSearch;
|
|
}
|
|
|
|
HRESULT CHistBand::_ClearSearch()
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (_pidlSearch)
|
|
{
|
|
if (_EnsureSearch())
|
|
{
|
|
EVAL(SUCCEEDED(_psfSearch->CancelAsyncSearch(_pidlSearch, NULL)));
|
|
hr = _psfSearch->InvalidateSearch(_pidlSearch, NULL);
|
|
}
|
|
ILFree(_pidlSearch);
|
|
_pidlSearch = NULL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CHistBand::_ExecuteSearch(LPTSTR pszSearchString)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (_EnsureSearch())
|
|
{
|
|
_ClearSearch();
|
|
hr = _psfSearch->FindString(pszSearchString,
|
|
NULL,
|
|
reinterpret_cast<IUnknown *>
|
|
(static_cast<IShellFolderSearchableCallback *>
|
|
(this)),
|
|
&_pidlSearch);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_ChangePidl(ILCombine(_pidlHistory, _pidlSearch));
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
#ifdef SPLIT_HISTORY_VIEW_BUTTON
|
|
UINT CHistBand::_NextMenuItem()
|
|
{
|
|
if (_uViewCheckedItem + 1 > _nViews)
|
|
return 1;
|
|
else
|
|
return _uViewCheckedItem + 1;
|
|
}
|
|
#endif
|
|
|
|
HRESULT CHistBand::_ViewPopupSelect(UINT idCmd)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (idCmd == MENUID_SEARCH)
|
|
{
|
|
if (_uViewCheckedItem != MENUID_SEARCH)
|
|
{
|
|
// display the dialog box
|
|
if (SUCCEEDED(hr = _DoSearchUIStuff()))
|
|
{
|
|
_ChangePidl((LPITEMIDLIST)INVALID_HANDLE_VALUE); // blank out NSC
|
|
_uViewCheckedItem = MENUID_SEARCH;
|
|
CheckMenuRadioItem(_hViewMenu, 1, _iMaxMenuID, _uViewCheckedItem, MF_BYCOMMAND);
|
|
}
|
|
}
|
|
if (_hwndSearchDlg)
|
|
SetFocus(GetDlgItem(_hwndSearchDlg, IDC_EDITHISTSEARCH));
|
|
}
|
|
else
|
|
{
|
|
LPCITEMIDLIST pidlNewSelect = _MenuIDToPIDL(idCmd);
|
|
if (pidlNewSelect)
|
|
{
|
|
if (ILIsEmpty(pidlNewSelect))
|
|
hr = _ChangePidl(ILClone(_pidlHistory));
|
|
else
|
|
hr = _ChangePidl(ILCombine(_pidlHistory, pidlNewSelect));
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = _SelectPidl(NULL, TRUE, pidlNewSelect);
|
|
|
|
// deleted "&& _uViewCheckedItem >= 0" from test below
|
|
// because UINTs are by definition always >= 0
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// get rid of search dialog -- its no longer needed
|
|
if (_hwndSearchDlg)
|
|
{
|
|
EndDialog(_hwndSearchDlg, 0);
|
|
DestroyWindow(_hwndSearchDlg);
|
|
_hwndSearchDlg = NULL;
|
|
// invalidate the previous search and prepare for the next
|
|
_ClearSearch();
|
|
RECT rcSelf;
|
|
GetClientRect(_hwnd, &rcSelf);
|
|
_ResizeChildWindows(rcSelf.right, rcSelf.bottom, TRUE);
|
|
}
|
|
|
|
_uViewCheckedItem = idCmd;
|
|
CheckMenuRadioItem(_hViewMenu, 1, _iMaxMenuID,
|
|
_uViewCheckedItem, MF_BYCOMMAND);
|
|
// write out the new selection to registry
|
|
EVAL(SUCCEEDED(_SetRegistryPersistView(_uViewCheckedItem)));
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CHistBand::_DoViewPopup(int x, int y)
|
|
{
|
|
if (!_hViewMenu) return E_FAIL;
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
UINT idCmd = TrackPopupMenu(_hViewMenu, TPM_RETURNCMD, x, y, 0, _hwnd, NULL);
|
|
// Currently, re-selecting the menu item will cause the item to be refreshed
|
|
// This makes sense to me, but it can be prevented by
|
|
// testing idCmd != _uViewCheckedItem
|
|
if ((idCmd > 0))
|
|
{
|
|
return _ViewPopupSelect(idCmd);
|
|
}
|
|
else
|
|
hr = S_FALSE;
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Change the current select NSC pidl
|
|
// WARNING: The pidl passed in will be assimilated by us...
|
|
// We will deallocate it.
|
|
HRESULT CHistBand::_ChangePidl(LPITEMIDLIST pidl)
|
|
{
|
|
if (_pidl)
|
|
ILFree(_pidl);
|
|
|
|
_pidl = pidl;
|
|
if ((LPITEMIDLIST)INVALID_HANDLE_VALUE == pidl)
|
|
_pidl = NULL;
|
|
_pns->Initialize(pidl, (SHCONTF_FOLDERS | SHCONTF_NONFOLDERS), (NSS_DROPTARGET | NSS_BROWSERSELECT));
|
|
return S_OK;
|
|
}
|
|
|
|
// _SelectPidl - Have NSC change the current selected pidl
|
|
//
|
|
// passing NULL for pidlSelect will select the current select pidl
|
|
HRESULT CHistBand::_SelectPidl(LPCITEMIDLIST pidlSelect, // <-Standard Hist-type pidl to select
|
|
BOOL fCreate, // <-create NSC item if not there?
|
|
LPCITEMIDLIST pidlView,/*=NULL*/ // <-special history view type or NULL
|
|
BOOL fReinsert /*=0*/) // <-reinsert pidl into NSC and re-sort
|
|
{
|
|
HRESULT hRes = S_OK;
|
|
LPITEMIDLIST pidlSelectToFree = NULL;
|
|
if (!pidlSelect)
|
|
{
|
|
pidlSelectToFree = _GetCurrentSelectPidl();
|
|
|
|
pidlSelect = pidlSelectToFree;
|
|
}
|
|
|
|
if (pidlSelect)
|
|
{
|
|
LPITEMIDLIST pidlNewSelect = NULL;
|
|
|
|
// cache the last selected pidl
|
|
if (_pidlLastSelect != pidlSelect)
|
|
{
|
|
if (_pidlLastSelect)
|
|
ILFree(_pidlLastSelect);
|
|
_pidlLastSelect = ILClone(pidlSelect);
|
|
}
|
|
|
|
if (pidlView && !ILIsEmpty(pidlView))
|
|
{
|
|
IShellFolderViewType *psfvtInfo = _GetViewTypeInfo();
|
|
if (psfvtInfo)
|
|
{
|
|
LPITEMIDLIST pidlFromRoot = ILFindChild(_pidlHistory, pidlSelect);
|
|
if (pidlFromRoot && !ILIsEmpty(pidlFromRoot))
|
|
{
|
|
LPITEMIDLIST pidlNewFromRoot;
|
|
if (SUCCEEDED(psfvtInfo->TranslateViewPidl(pidlFromRoot, pidlView, &pidlNewFromRoot)))
|
|
{
|
|
if (pidlNewFromRoot)
|
|
{
|
|
pidlNewSelect = ILCombine(_pidlHistory, pidlNewFromRoot);
|
|
if (pidlNewSelect)
|
|
{
|
|
_pns->SetSelectedItem(pidlNewSelect, fCreate, fReinsert, 0);
|
|
ILFree(pidlNewSelect);
|
|
}
|
|
ILFree(pidlNewFromRoot);
|
|
}
|
|
}
|
|
}
|
|
psfvtInfo->Release();
|
|
}
|
|
}
|
|
else
|
|
_pns->SetSelectedItem(pidlSelect, fCreate, fReinsert, 0);
|
|
|
|
ILFree(pidlSelectToFree);
|
|
}
|
|
return hRes;
|
|
}
|
|
|
|
HRESULT CHistBand::_SetRegistryPersistView(int iMenuID)
|
|
{
|
|
LPCITEMIDLIST pidlReg = _MenuIDToPIDL(iMenuID);
|
|
if (!pidlReg)
|
|
return E_FAIL;
|
|
|
|
LONG lRet = (SHRegSetUSValue(REGSTR_PATH_MAIN, REGKEY_HISTORY_VIEW,
|
|
REG_BINARY, (LPVOID)pidlReg, ILGetSize(pidlReg),
|
|
SHREGSET_HKCU | SHREGSET_FORCE_HKCU));
|
|
return HRESULT_FROM_WIN32(lRet);
|
|
}
|
|
|
|
// Get the default view from the registry as a menu item
|
|
int CHistBand::_GetRegistryPersistView()
|
|
{
|
|
int iRegMenu = -1;
|
|
DWORD dwType = REG_BINARY;
|
|
|
|
ITEMIDLIST pidlDefault = { 0 };
|
|
|
|
// make a preliminary call to find out the size of the data
|
|
DWORD cbData = 0;
|
|
LONG error = SHRegGetUSValue(REGSTR_PATH_MAIN, REGKEY_HISTORY_VIEW, &dwType,
|
|
NULL, &cbData, FALSE, &pidlDefault,
|
|
sizeof(pidlDefault));
|
|
if (cbData)
|
|
{
|
|
LPITEMIDLIST pidlReg = ((LPITEMIDLIST)SHAlloc(cbData));
|
|
if (pidlReg)
|
|
{
|
|
error = SHRegGetUSValue(REGSTR_PATH_MAIN, REGKEY_HISTORY_VIEW, &dwType,
|
|
(LPVOID)pidlReg, &cbData, FALSE, &pidlDefault,
|
|
sizeof(pidlDefault));
|
|
|
|
if (error == ERROR_SUCCESS)
|
|
iRegMenu = _PIDLToMenuID(pidlReg);
|
|
|
|
SHFree(pidlReg);
|
|
}
|
|
}
|
|
|
|
return iRegMenu;
|
|
}
|
|
|
|
LPCITEMIDLIST CHistBand::_MenuIDToPIDL(UINT uMenuID)
|
|
{
|
|
ASSERT(_ppidlViewTypes);
|
|
if ((uMenuID > 0) && (uMenuID <= _nViews))
|
|
return _ppidlViewTypes[uMenuID - 1];
|
|
return NULL;
|
|
}
|
|
|
|
int CHistBand::_PIDLToMenuID(LPITEMIDLIST pidl)
|
|
{
|
|
ASSERT(_psfHistory && _ppidlViewTypes);
|
|
|
|
int iMenuID = -1;
|
|
|
|
// handle the empty pidl, which designates the
|
|
// default view, separately
|
|
if (ILIsEmpty(pidl))
|
|
iMenuID = 1;
|
|
else
|
|
{
|
|
for (UINT u = 0; u < _nViews; ++u)
|
|
{
|
|
if (ShortFromResult(_psfHistory->CompareIDs(0, pidl, _ppidlViewTypes[u])) == 0)
|
|
iMenuID = u + 1;
|
|
}
|
|
}
|
|
return iMenuID;
|
|
}
|
|
|
|
// remember to release return value
|
|
IShellFolderViewType* CHistBand::_GetViewTypeInfo()
|
|
{
|
|
IShellFolderViewType* psfvRet = NULL;
|
|
|
|
if (_psfvtCache)
|
|
{
|
|
_psfvtCache->AddRef();
|
|
psfvRet = _psfvtCache;
|
|
}
|
|
else if (_psfHistory)
|
|
{
|
|
// QI For the views
|
|
// We set the pointer because of a bad QI somewhere...
|
|
if (SUCCEEDED(_psfHistory->QueryInterface(IID_PPV_ARG(IShellFolderViewType, &psfvRet))))
|
|
{
|
|
_psfvtCache = psfvRet;
|
|
psfvRet->AddRef(); // one released in destructor, another by caller
|
|
}
|
|
else
|
|
psfvRet = NULL;
|
|
}
|
|
return psfvRet;
|
|
}
|
|
|
|
HRESULT CHistBand::_FreeViewInfo()
|
|
{
|
|
if (_ppidlViewTypes)
|
|
{
|
|
// the first pidl in this list is NULL, the default view
|
|
for (UINT u = 0; u < _nViews; ++u)
|
|
{
|
|
ILFree(_ppidlViewTypes[u]);
|
|
}
|
|
LocalFree(_ppidlViewTypes);
|
|
_ppidlViewTypes = NULL;
|
|
}
|
|
|
|
if (_ppszStrViewNames)
|
|
{
|
|
for (UINT u = 0; u < _nViews; ++u)
|
|
{
|
|
if (EVAL(_ppszStrViewNames[u]))
|
|
CoTaskMemFree(_ppszStrViewNames[u]);
|
|
}
|
|
LocalFree(_ppszStrViewNames);
|
|
_ppszStrViewNames = NULL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// Load the popup menu (if there are views to be had)
|
|
HRESULT CHistBand::_InitViewPopup()
|
|
{
|
|
HRESULT hRes = E_FAIL;
|
|
|
|
_iMaxMenuID = 0;
|
|
|
|
if (SUCCEEDED((hRes = _GetHistoryViews())))
|
|
{
|
|
if ((_hViewMenu = CreatePopupMenu()))
|
|
{
|
|
// the IDCMD for the view menu will always be
|
|
// one more than the index into the view tables
|
|
for (UINT u = 0; u < _nViews; ++u)
|
|
{
|
|
int iMenuID = _PIDLToMenuID(_ppidlViewTypes[u]);
|
|
if (iMenuID >= 0)
|
|
AppendMenu(_hViewMenu, MF_STRING, iMenuID, _ppszStrViewNames[u]);
|
|
|
|
if (iMenuID > _iMaxMenuID)
|
|
_iMaxMenuID = iMenuID;
|
|
}
|
|
|
|
// retrieve the persisted view information
|
|
// and check the corresponding menu item
|
|
int iSelectMenuID = _GetRegistryPersistView();
|
|
if (iSelectMenuID < 0 || ((UINT)iSelectMenuID) > _nViews)
|
|
iSelectMenuID = 1; //bogus menuid
|
|
_uViewCheckedItem = iSelectMenuID;
|
|
CheckMenuRadioItem(_hViewMenu, 1, _nViews, _uViewCheckedItem, MF_BYCOMMAND);
|
|
}
|
|
}
|
|
|
|
#ifdef HISTORY_VIEWSEARCHMENU
|
|
// if this is a searchable shell folder, then add the search menu item
|
|
if (_EnsureSearch())
|
|
{
|
|
hRes = S_OK;
|
|
|
|
// only add separator if there is a menu already!
|
|
if (!_hViewMenu)
|
|
_hViewMenu = CreatePopupMenu();
|
|
else
|
|
AppendMenu(_hViewMenu, MF_SEPARATOR, 0, NULL);
|
|
|
|
if (_hViewMenu)
|
|
{
|
|
TCHAR szSearchMenuText[MAX_PATH];
|
|
LoadString(MLGetHinst(), IDS_SEARCH_MENUOPT,
|
|
szSearchMenuText, ARRAYSIZE(szSearchMenuText));
|
|
AppendMenu(_hViewMenu, MF_STRING, MENUID_SEARCH, szSearchMenuText);
|
|
_iMaxMenuID = MENUID_SEARCH;
|
|
}
|
|
else
|
|
hRes = E_FAIL;
|
|
}
|
|
#endif
|
|
return hRes;
|
|
}
|
|
|
|
// This guy calls the enumerator
|
|
HRESULT CHistBand::_GetHistoryViews()
|
|
{
|
|
ASSERT(_psfHistory);
|
|
HRESULT hRes = E_FAIL;
|
|
|
|
UINT cbViews; // how many views are allocated
|
|
|
|
ASSERT(VIEWTYPE_MAX > 0);
|
|
|
|
EVAL(SUCCEEDED(_FreeViewInfo()));
|
|
|
|
IShellFolderViewType *psfViewType = _GetViewTypeInfo();
|
|
if (psfViewType)
|
|
{
|
|
// allocate buffers to store the view information
|
|
_ppidlViewTypes = ((LPITEMIDLIST *)LocalAlloc(LPTR, VIEWTYPE_MAX * sizeof(LPITEMIDLIST)));
|
|
if (_ppidlViewTypes)
|
|
{
|
|
_ppszStrViewNames = ((LPTSTR *)LocalAlloc(LPTR, VIEWTYPE_MAX * sizeof(LPTSTR)));
|
|
if (_ppszStrViewNames)
|
|
{
|
|
IEnumIDList *penum = NULL;
|
|
cbViews = VIEWTYPE_MAX;
|
|
_nViews = 1;
|
|
// get the default view information
|
|
_ppidlViewTypes[0] = IEILCreate(sizeof(ITEMIDLIST));
|
|
if (_ppidlViewTypes[0] &&
|
|
SUCCEEDED((hRes = psfViewType->GetDefaultViewName(0, &(_ppszStrViewNames[0])))))
|
|
{
|
|
// empty pidl will be the default
|
|
ASSERT(ILIsEmpty(_ppidlViewTypes[0]));
|
|
// get the iterator for the other views
|
|
if (SUCCEEDED((hRes = psfViewType->EnumViews(0, &penum))))
|
|
{
|
|
ULONG cFetched = 0;
|
|
// iterate to get other view information
|
|
while (SUCCEEDED(hRes) &&
|
|
SUCCEEDED(penum->Next(1, &(_ppidlViewTypes[_nViews]), &cFetched)) &&
|
|
cFetched)
|
|
{
|
|
// get the name of this view
|
|
if (SUCCEEDED(DisplayNameOfAsOLESTR(_psfHistory, _ppidlViewTypes[_nViews], 0, &(_ppszStrViewNames[_nViews]))))
|
|
{
|
|
// prepare for next iteration by reallocating the buffer if necessary
|
|
if (_nViews > cbViews - 1)
|
|
{
|
|
LPITEMIDLIST *ppidlViewTypes = ((LPITEMIDLIST *)LocalReAlloc(_ppidlViewTypes,
|
|
(cbViews + VIEWTYPE_REALLOC) * sizeof(LPITEMIDLIST),
|
|
LMEM_MOVEABLE | LMEM_ZEROINIT));
|
|
if (ppidlViewTypes)
|
|
{
|
|
_ppidlViewTypes = ppidlViewTypes;
|
|
LPTSTR * ppszStrViewNames = ((LPTSTR *)LocalReAlloc(_ppszStrViewNames,
|
|
(cbViews + VIEWTYPE_REALLOC) * sizeof(LPTSTR),
|
|
LMEM_MOVEABLE | LMEM_ZEROINIT));
|
|
if (ppszStrViewNames)
|
|
{
|
|
_ppszStrViewNames = ppszStrViewNames;
|
|
cbViews += VIEWTYPE_REALLOC;
|
|
}
|
|
else
|
|
{
|
|
hRes = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hRes = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
}
|
|
++_nViews;
|
|
}
|
|
}
|
|
penum->Release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
psfViewType->Release();
|
|
}
|
|
return hRes;
|
|
}
|
|
|
|
HRESULT CHistBand_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
|
|
{
|
|
// aggregation checking is handled in class factory
|
|
CHistBand * phb = new CHistBand();
|
|
if (!phb)
|
|
return E_OUTOFMEMORY;
|
|
|
|
ASSERT(phb->_pidlHistory == NULL &&
|
|
phb->_pidlLastSelect == NULL &&
|
|
phb->_pidl == NULL &&
|
|
phb->_psfvtCache == NULL);
|
|
|
|
|
|
if (SUCCEEDED(SHGetHistoryPIDL(&(phb->_pidlHistory))) &&
|
|
SUCCEEDED(IEBindToObject(phb->_pidlHistory,
|
|
&(phb->_psfHistory))))
|
|
{
|
|
HRESULT hResLocal = E_FAIL;
|
|
|
|
// if we can get different views, then init with the persisted
|
|
// view type, otherwise, init with the top-level history type
|
|
if (SUCCEEDED(phb->_InitViewPopup()))
|
|
{
|
|
LPCITEMIDLIST pidlInit = phb->_MenuIDToPIDL(phb->_uViewCheckedItem);
|
|
if (pidlInit)
|
|
{
|
|
LPITEMIDLIST pidlFullInit = ILCombine(phb->_pidlHistory, pidlInit);
|
|
if (pidlFullInit)
|
|
{
|
|
hResLocal = phb->_Init(pidlFullInit);
|
|
ILFree(pidlFullInit);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
hResLocal = phb->_Init(phb->_pidlHistory);
|
|
|
|
// From old favband code: // if (SUCCEEDED(phb->_Init((LPCITEMIDLIST)CSIDL_FAVORITES)))
|
|
if (SUCCEEDED(hResLocal))
|
|
{
|
|
phb->_pns = CNscTree_CreateInstance();
|
|
if (phb->_pns)
|
|
{
|
|
ASSERT(poi);
|
|
phb->_poi = poi;
|
|
// if you change this cast, fix up CChannelBand_CreateInstance
|
|
*ppunk = SAFECAST(phb, IDeskBand *);
|
|
|
|
IUnknown_SetSite(phb->_pns, *ppunk);
|
|
phb->_SetNscMode(MODE_HISTORY);
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
phb->Release();
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Ask the powers that be which pidl is selected...
|
|
LPITEMIDLIST CHistBand::_GetCurrentSelectPidl(IOleCommandTarget *poctProxy/* = NULL*/)
|
|
{
|
|
LPITEMIDLIST pidlRet = NULL;
|
|
VARIANT var;
|
|
BOOL fReleaseProxy = FALSE;
|
|
VariantInit(&var);
|
|
var.vt = VT_EMPTY;
|
|
|
|
if (poctProxy == NULL)
|
|
{
|
|
IBrowserService *pswProxy;
|
|
if (SUCCEEDED(QueryService(SID_SProxyBrowser, IID_PPV_ARG(IBrowserService, &pswProxy))))
|
|
{
|
|
ASSERT(pswProxy);
|
|
if (FAILED(pswProxy->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &poctProxy))))
|
|
{
|
|
pswProxy->Release();
|
|
return NULL;
|
|
}
|
|
else
|
|
fReleaseProxy = TRUE;
|
|
|
|
pswProxy->Release();
|
|
}
|
|
}
|
|
|
|
// Inquire the current select pidl
|
|
if (poctProxy && (SUCCEEDED(poctProxy->Exec(&CGID_Explorer, SBCMDID_GETHISTPIDL, OLECMDEXECOPT_PROMPTUSER, NULL, &var))) &&
|
|
(var.vt != VT_EMPTY))
|
|
{
|
|
pidlRet = VariantToIDList(&var);
|
|
VariantClearLazy(&var);
|
|
}
|
|
if (fReleaseProxy)
|
|
poctProxy->Release();
|
|
return pidlRet;
|
|
}
|
|
|
|
// gets called by CNSCBand::ShowDW every time history band is shown
|
|
HRESULT CHistBand::_OnRegisterBand(IOleCommandTarget *poctProxy)
|
|
{
|
|
HRESULT hRes = E_FAIL;
|
|
if (_uViewCheckedItem != MENUID_SEARCH)
|
|
{
|
|
LPITEMIDLIST pidlSelect = _GetCurrentSelectPidl(poctProxy);
|
|
if (pidlSelect)
|
|
{
|
|
_SelectPidl(pidlSelect, TRUE);
|
|
ILFree(pidlSelect);
|
|
hRes = S_OK;
|
|
}
|
|
}
|
|
return hRes;
|
|
}
|
|
|
|
HRESULT CHistBand::_InitializeNsc()
|
|
{
|
|
return _pns->Initialize(_pidl, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, NSS_NOHISTSELECT | NSS_DROPTARGET | NSS_BROWSERSELECT);
|
|
}
|
|
|
|
BOOL CHistBand::_ShouldNavigateToPidl(LPCITEMIDLIST pidl, ULONG ulAttrib)
|
|
{
|
|
return !(ulAttrib & SFGAO_FOLDER);
|
|
}
|
|
|
|
HRESULT CHistBand::_NavigateRightPane(IShellBrowser *psb, LPCITEMIDLIST pidl)
|
|
{
|
|
HRESULT hr = psb->BrowseObject(pidl, SBSP_SAMEBROWSER | SBSP_NOAUTOSELECT);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IOleCommandTarget *poctProxy;
|
|
if (SUCCEEDED(QueryService(SID_SProxyBrowser, IID_PPV_ARG(IOleCommandTarget, &poctProxy))))
|
|
{
|
|
VARIANTARG var;
|
|
|
|
InitVariantFromIDList(&var, pidl);
|
|
poctProxy->Exec(&CGID_Explorer, SBCMDID_SELECTHISTPIDL, OLECMDEXECOPT_PROMPTUSER, &var, NULL);
|
|
VariantClear(&var);
|
|
poctProxy->Release();
|
|
}
|
|
UEMFireEvent(&UEMIID_BROWSER, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_NAVIGATE, UIBL_NAVHIST);
|
|
}
|
|
return hr;
|
|
}
|