windows-nt/Source/XPSP1/NT/shell/browseui/addrlist.cpp
2020-09-26 16:20:57 +08:00

685 lines
19 KiB
C++

/**************************************************************\
FILE: addrlist.cpp
DESCRIPTION:
This is a class that all Address Lists can inherite
from. This will give them the IAddressList interface
so they can work in the AddressBand/Bar.
\**************************************************************/
#include "priv.h"
#include "util.h"
#include "itbdrop.h"
#include "autocomp.h"
#include "addrlist.h"
#include "apithk.h"
#include "shui.h"
#include "shlguid.h"
CAddressList::CAddressList() : _cRef(1)
{
// This needs to be allocated in Zero Inited Memory.
// Assert that all Member Variables are inited to Zero.
ASSERT(!_pbp);
}
CAddressList::~CAddressList()
{
if (_pbp)
_pbp->Release();
if (_pbs)
_pbs->Release();
if (_pshuUrl)
delete _pshuUrl;
}
ULONG CAddressList::AddRef()
{
_cRef++;
return _cRef;
}
ULONG CAddressList::Release()
{
ASSERT(_cRef > 0);
_cRef--;
if (_cRef > 0)
return _cRef;
delete this;
return 0;
}
HRESULT CAddressList::QueryInterface(REFIID riid, void **ppvObj)
{
if (IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_IWinEventHandler) ||
IsEqualIID(riid, IID_IAddressList))
{
*ppvObj = SAFECAST(this, IAddressList*);
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
//================================
// ** IWinEventHandler Interface ***
/****************************************************\
FUNCTION: OnWinEvent
DESCRIPTION:
This function will give receive events from
the parent ShellToolbar.
\****************************************************/
HRESULT CAddressList::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
{
LRESULT lres = 0;
switch (uMsg)
{
case WM_COMMAND:
lres = _OnCommand(wParam, lParam);
break;
case WM_NOTIFY:
lres = _OnNotify((LPNMHDR)lParam);
break;
}
if (plres)
*plres = lres;
return S_OK;
}
//================================
// *** IAddressList Interface ***
/****************************************************\
FUNCTION: Connect
DESCRIPTION:
We are either becoming the selected list for
the AddressBand's combobox, or lossing this status.
We need to populate or unpopulate the combobox
as appropriate.
\****************************************************/
HRESULT CAddressList::Connect(BOOL fConnect, HWND hwnd, IBrowserService * pbs, IBandProxy * pbp, IAutoComplete * pac)
{
HRESULT hr = S_OK;
ASSERT(hwnd);
_fVisible = fConnect;
_hwnd = hwnd;
// Copy the (IBandProxy *) parameter
if (_pbp)
_pbp->Release();
_pbp = pbp;
if (_pbp)
_pbp->AddRef();
if (_pbs)
_pbs->Release();
_pbs = pbs;
if (_pbs)
_pbs->AddRef();
if (fConnect)
{
//
// Initial combobox parameters.
//
_InitCombobox();
}
else
{
// Remove contents of the List
_PurgeComboBox();
}
return hr;
}
/****************************************************\
FUNCTION: _InitCombobox
DESCRIPTION:
Prepare the combo box for this list. This normally
means that the indenting and icon are either turned
on or off.
\****************************************************/
void CAddressList::_InitCombobox()
{
SendMessage(_hwnd, CB_SETDROPPEDWIDTH, 200, 0L);
SendMessage(_hwnd, CB_SETEXTENDEDUI, TRUE, 0L);
SendMessage(_hwnd, CBEM_SETEXTENDEDSTYLE, CBES_EX_NOSIZELIMIT, CBES_EX_NOSIZELIMIT);
}
/****************************************************\
FUNCTION: _PurgeComboBox
DESCRIPTION:
Removes all items from the combobox.
\****************************************************/
void CAddressList::_PurgeComboBox()
{
if (_hwnd)
{
SendMessage(_hwnd, CB_RESETCONTENT, 0, 0L);
}
}
/****************************************************\
FUNCTION: _OnCommand
DESCRIPTION:
This function will handle WM_COMMAND messages.
\****************************************************/
LRESULT CAddressList::_OnCommand(WPARAM wParam, LPARAM lParam)
{
UINT uCmd = GET_WM_COMMAND_CMD(wParam, lParam);
switch (uCmd)
{
case CBN_DROPDOWN:
{
HCURSOR hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
//
// DOH! The user wants to see the full combo contents.
// Better go fill it in now.
//
_Populate();
SetCursor(hCursorOld);
}
break;
}
return 0;
}
void ChangeUrl(LPCTSTR pszUrl, LPTSTR pszDisplayName, DWORD cchSize)
{
TCHAR szTemp[MAX_URL_STRING];
StrCpyN(szTemp, pszUrl, ARRAYSIZE(szTemp)); // This is necessary because pszUrl points into the buffer of pszDisplayName
StrCpyN(pszDisplayName, szTemp, cchSize);
}
/****************************************************\
FUNCTION: NavigationComplete
DESCRIPTION:
Update the URL in the Top of the list.
\****************************************************/
HRESULT CAddressList::NavigationComplete(LPVOID pvCShellUrl)
{
HRESULT hr = S_OK;
TCHAR szDisplayName[MAX_URL_STRING];
CShellUrl * psu = (CShellUrl *) pvCShellUrl;
LPITEMIDLIST pidl;
ASSERT(pvCShellUrl);
hr = psu->GetDisplayName(szDisplayName, SIZECHARS(szDisplayName));
if (SUCCEEDED(hr))
{
//
// Don't display the url to internal error pages. The url that should get
// displayed is appended after the #.
//
// All error urls start with res:// so do a quick check first.
//
BOOL fChangeURL = TRUE;
if (TEXT('r') == szDisplayName[0] && TEXT('e') == szDisplayName[1])
{
if (IsErrorUrl(szDisplayName))
{
TCHAR* pszUrl = StrChr(szDisplayName, TEXT('#'));
if (pszUrl)
{
pszUrl += 1;
DWORD dwScheme = GetUrlScheme(pszUrl);
fChangeURL = ((URL_SCHEME_HTTP == dwScheme) ||
(URL_SCHEME_HTTPS == dwScheme) ||
(URL_SCHEME_FTP == dwScheme) ||
(URL_SCHEME_GOPHER == dwScheme));
// Don't blast in the stuff after the # into address bar
// unless it is a 'safe' url. If it's not safe leave the
// addressbar alone to preserve what the user typed in.
//
// The issue here is that a web page could navigate to our internal
// error page with "format c:" after the '#'. The error page
// suggests that the user refreshed the page which would be very bad!
if (fChangeURL)
{
ChangeUrl(pszUrl, szDisplayName, ARRAYSIZE(szDisplayName));
}
}
}
}
if (fChangeURL)
{
SHRemoveURLTurd(szDisplayName);
SHCleanupUrlForDisplay(szDisplayName);
hr = psu->GetPidl(&pidl);
if (SUCCEEDED(hr))
{
COMBOBOXEXITEM cbexItem = {0};
cbexItem.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
cbexItem.iItem = -1;
cbexItem.pszText = szDisplayName;
cbexItem.cchTextMax = ARRAYSIZE(szDisplayName);
hr = _GetPidlIcon(pidl, &(cbexItem.iImage), &(cbexItem.iSelectedImage));
SendMessage(_hwnd, CBEM_SETITEM, (WPARAM)0, (LPARAM)(LPVOID)&cbexItem);
ILFree(pidl);
}
}
}
TraceMsg(TF_BAND|TF_GENERAL, "CAddressList: NavigationComplete(), URL=%s", szDisplayName);
return hr;
}
/*******************************************************************
FUNCTION: _MoveAddressToTopOfList
PARAMETERS:
iSel - index of item in combo box to move
DESCRIPTION:
Moves the specified selection in the combo box
to be the first item in the combo box
********************************************************************/
BOOL CAddressList::_MoveAddressToTopOfList(int iSel)
{
BOOL fRet = FALSE;
ASSERT(iSel >= 0); // must have valid index
COMBOBOXEXITEM cbexItem = {0};
TCHAR szAddress[MAX_URL_STRING+1];
cbexItem.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
cbexItem.pszText = szAddress;
cbexItem.cchTextMax = ARRAYSIZE(szAddress);
cbexItem.iItem = iSel;
// get the specified item from combo box
if (SendMessage(_hwnd,CBEM_GETITEM,0,(LPARAM) &cbexItem)) {
SendMessage(_hwnd, CBEM_DELETEITEM, (WPARAM)iSel, (LPARAM)0);
// re-insert it at index 0 to put it at the top
cbexItem.iItem = 0;
// sending CBEM_INSERTITEM should return the index we specified
// (0) if successful
fRet = (SendMessage(_hwnd, CBEM_INSERTITEM, (WPARAM)0,
(LPARAM)(LPVOID)&cbexItem) == 0);
}
return fRet;
}
/*******************************************************************
FUNCTION: _ComboBoxInsertURL
DESCRIPTION:
Adds the specified URL to the top of the address bar
combo box. Limits the number of URLs in combo box to
nMaxComboBoxSize.
********************************************************************/
void CAddressList::_ComboBoxInsertURL(LPCTSTR pszURL, int cchStrSize, int nMaxComboBoxSize)
{
// Since we own it and it's populated,
// we will add it directly to the ComboBox.
int iPrevInstance;
int iImage, iSelectedImage ;
COMBOBOXEXITEM cbexItem = {0};
cbexItem.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
cbexItem.iItem = 0;
cbexItem.cchTextMax = cchStrSize;
cbexItem.pszText = (LPTSTR)pszURL;
_GetUrlUI(NULL, (LPTSTR)pszURL, &iImage, &iSelectedImage);
cbexItem.iImage = iImage;
cbexItem.iSelectedImage = iSelectedImage;
iPrevInstance = (int)SendMessage(_hwnd, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)pszURL);
if (iPrevInstance != CB_ERR) {
_MoveAddressToTopOfList(iPrevInstance);
return;
}
// insert the URL as the first item in combo box
SendMessage(_hwnd, CBEM_INSERTITEM, (WPARAM)0, (LPARAM)(LPVOID)&cbexItem);
// limit number of items in combo box to nMaxComboBoxSize
if (ComboBox_GetCount(_hwnd) > nMaxComboBoxSize)
{
// if we're ever over the limit, we should only be over the limit
// by exactly one item
ASSERT(ComboBox_GetCount(_hwnd) == nMaxComboBoxSize+1);
// if over the limit, delete the least recently used
// (the one with the highest index)
SendMessage(_hwnd, CBEM_DELETEITEM, (WPARAM)nMaxComboBoxSize, (LPARAM)0);
}
}
/*******************************************************************
FUNCTION: SetToListIndex
DESCRIPTION:
This function will set the CShellUrl parameter to the item
in the Drop Down list that is indexed by nIndex.
********************************************************************/
HRESULT CAddressList::SetToListIndex(int nIndex, LPVOID pvShelLUrl)
{
HRESULT hr = S_OK;
TCHAR szBuffer[MAX_URL_STRING];
CShellUrl * psuURL = (CShellUrl *) pvShelLUrl;
if (SUCCEEDED(GetCBListIndex(_hwnd, nIndex, szBuffer, SIZECHARS(szBuffer))))
{
hr = psuURL->ParseFromOutsideSource(szBuffer, SHURL_FLAGS_NOUI);
ASSERT(SUCCEEDED(hr)); // We should not have added it to the Drop Down list if it's invalid.
}
return hr;
}
HRESULT CAddressList::FileSysChangeAL(DWORD dw, LPCITEMIDLIST *ppidl)
{
return S_OK;
}
/****************************************************\
FUNCTION: GetCBListIndex
DESCRIPTION:
This function will get the text of a specified
element in the combobox.
\****************************************************/
HRESULT GetCBListIndex(HWND hwnd, int iItem, LPTSTR szAddress, int cchAddressSize)
{
HRESULT hr = E_FAIL;
COMBOBOXEXITEM cbexItem = {0};
cbexItem.mask = CBEIF_TEXT;
cbexItem.pszText = szAddress;
cbexItem.cchTextMax = cchAddressSize;
cbexItem.iItem = iItem;
if (SendMessage(hwnd, CBEM_GETITEM, 0, (LPARAM) &cbexItem))
hr = S_OK;
return hr;
}
// Helper Function
// We need to really becareful of perf in this function.
HRESULT CAddressList::_GetUrlUI(CShellUrl *psu, LPCTSTR szUrl, int *piImage, int *piSelectedImage)
{
CShellUrl * psuUrl;
HRESULT hr = E_FAIL;
if (psu)
psuUrl = psu;
else
{
psuUrl = new CShellUrl();
if (psuUrl)
{
// Set the parent for error messageboxes. Note that this could end up disabing the taskbar.
// If this is deemed to be a problem we can first check to see where the addressbar is hosted.
psuUrl->SetMessageBoxParent(_hwnd);
SetDefaultShellPath(psuUrl);
}
}
//Initialize the values to 0
*piImage = 0;
*piSelectedImage = 0;
//if object is not created return with default value
if (!psuUrl)
return E_OUTOFMEMORY;
#ifdef DISABLED // Why not show the correct icon for removable drives?
int iDrive;
// See if we have a drive specified in the path
if ((iDrive = PathGetDriveNumber(szUrl)) >= 0)
{
// See if the drive is removable ?
if(DriveType(iDrive) == DRIVE_REMOVABLE)
hr = S_OK; //Drive is removable so pass the default icons
}
#endif
// Do we still need to get the icons?
if (FAILED(hr))
{
// Yes, so try the fast way first.
hr = _GetFastPathIcons(szUrl, piImage, piSelectedImage);
if (FAILED(hr))
{
LPITEMIDLIST pidl = NULL;
// If that failed because it the string probably uses advanced parsing,
// let CShellUrl do it the slower but more thurough way.
hr = psuUrl->ParseFromOutsideSource(szUrl, SHURL_FLAGS_NOUI);
if(SUCCEEDED(hr))
hr = psuUrl->GetPidl(&pidl);
if(SUCCEEDED(hr))
{
hr = _GetPidlIcon(pidl, piImage, piSelectedImage);
ILFree(pidl);
}
}
}
if (psu != psuUrl)
delete psuUrl;
return hr;
}
// IECreateFromPath() and CShellUrl::ParseFromOutsideSource() both
// touch the disk which causes unconnected network cases to be really
// slow. This will create icons for file system paths w/o hitting
// the disk.
HRESULT CAddressList::_GetFastPathIcons(LPCTSTR pszPath, int *piImage, int *piSelectedImage)
{
SHFILEINFO shFileInfo = {0};
// SHGetFileInfo() with those flags will be fast because it's won't filter out
// garbage passed to it. So it will think URLs are actually relative paths
// and accept them. We will fall back to the slow advanced parser which is still
// fast with URLs.
if (PathIsRelative(pszPath))
return E_FAIL;
HIMAGELIST himl = (HIMAGELIST) SHGetFileInfo(pszPath, FILE_ATTRIBUTE_DIRECTORY, &shFileInfo, sizeof(shFileInfo), (SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES));
if (!himl || !shFileInfo.iIcon)
return E_FAIL;
*piImage = shFileInfo.iIcon;
*piSelectedImage = shFileInfo.iIcon;
// I don't need to free himl.
return S_OK;
}
HRESULT CAddressList::_GetPidlIcon(LPCITEMIDLIST pidl, int *piImage, int *piSelectedImage)
{
IShellFolder *psfParent;
LPCITEMIDLIST pidlChild;
HRESULT hr = IEBindToParentFolder(pidl, &psfParent, &pidlChild);
if (SUCCEEDED(hr))
{
*piImage = IEMapPIDLToSystemImageListIndex(psfParent, pidlChild, piSelectedImage);
psfParent->Release();
}
return hr;
}
LPITEMIDLIST CAddressList::_GetDragDropPidl(LPNMCBEDRAGBEGINW pnmcbe)
{
LPITEMIDLIST pidl = NULL;
CShellUrl *psuUrl = new CShellUrl();
if (psuUrl)
{
// Set the parent for error messageboxes. Note that this could end up disabing the taskbar.
// If this is deemed to be a problem we can first check to see where the addressbar is hosted.
psuUrl->SetMessageBoxParent(_hwnd);
HRESULT hr = SetDefaultShellPath(psuUrl);
if (SUCCEEDED(hr))
{
hr = psuUrl->ParseFromOutsideSource(pnmcbe->szText, SHURL_FLAGS_NOUI, NULL);
if (SUCCEEDED(hr))
{
hr = psuUrl->GetPidl(&pidl);
}
}
delete psuUrl;
}
return pidl;
}
LRESULT CAddressList::_OnDragBeginA(LPNMCBEDRAGBEGINA pnmcbe)
{
NMCBEDRAGBEGINW nmcbew;
nmcbew.hdr = pnmcbe->hdr;
nmcbew.iItemid = pnmcbe->iItemid;
SHAnsiToUnicode(pnmcbe->szText, nmcbew.szText, SIZECHARS(nmcbew.szText));
return _OnDragBeginW(&nmcbew);
}
#ifdef UNIX
extern "C" LPITEMIDLIST UnixUrlToPidl(UINT uiCP, LPCTSTR pszUrl, LPCWSTR pszLocation);
#endif
LRESULT CAddressList::_OnDragBeginW(LPNMCBEDRAGBEGINW pnmcbe)
{
LPITEMIDLIST pidl = _GetDragDropPidl(pnmcbe);
#ifdef UNIX
// for UNIX we fake a URL pidl so we create a .url instead of a .lnk
if (!IsURLChild(pidl, TRUE))
{
LPITEMIDLIST pidl1;
TCHAR szPath[MAX_PATH];
StrCpyN(szPath, TEXT("file://"), 8);
SHGetPathFromIDList(pidl, &szPath[7]);
pidl1 = UnixUrlToPidl(CP_ACP, szPath, NULL);
if (pidl1)
{
ILFree(pidl);
pidl = pidl1;
}
}
#endif
if (pidl)
{
IOleCommandTarget *pcmdt = NULL;
IUnknown *punk;
if (_pbp && SUCCEEDED(_pbp->GetBrowserWindow(&punk)))
{
punk->QueryInterface(IID_IOleCommandTarget, (void **)&pcmdt);
punk->Release();
}
DoDragDropWithInternetShortcut(pcmdt, pidl, _hwnd);
if (pcmdt)
pcmdt->Release();
ILFree(pidl);
}
return 0;
}
// handle WM_NOTIFY messages.
LRESULT CAddressList::_OnNotify(LPNMHDR pnm)
{
LRESULT lReturn = 0;
switch (pnm->code)
{
case NM_SETCURSOR:
if (!SendMessage(_hwnd, CBEM_GETEXTENDEDSTYLE, 0, 0) & CBES_EX_NOEDITIMAGE)
{
RECT rc;
POINT pt;
int cx, cy;
GetCursorPos(&pt);
GetClientRect(_hwnd, &rc);
MapWindowRect(_hwnd, HWND_DESKTOP, &rc);
ImageList_GetIconSize((HIMAGELIST)SendMessage(_hwnd, CBEM_GETIMAGELIST, 0, 0), &cx, &cy);
rc.right = rc.left + cx + GetSystemMetrics(SM_CXEDGE);
if (PtInRect(&rc, pt))
{
// this means there's an image, which means we can drag
SetCursor(LoadHandCursor(0));
return 1;
}
}
break;
case CBEN_DRAGBEGINA:
lReturn = _OnDragBeginA((LPNMCBEDRAGBEGINA)pnm);
break;
case CBEN_DRAGBEGINW:
lReturn = _OnDragBeginW((LPNMCBEDRAGBEGINW)pnm);
break;
}
return lReturn;
}