|
|
/**************************************************************\
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];
StringCchCopy(szTemp, ARRAYSIZE(szTemp), pszUrl); // This is necessary because pszUrl points into the buffer of pszDisplayName
StringCchCopy(pszDisplayName, cchSize, szTemp); }
/****************************************************\
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); }
LRESULT CAddressList::_OnDragBeginW(LPNMCBEDRAGBEGINW pnmcbe) { LPITEMIDLIST pidl = _GetDragDropPidl(pnmcbe); 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; }
|