/**************************************************************\ FILE: address.cpp DESCRIPTION: The Class CAddressBand exists to support the Address ToolBand in either the main browser toolbar or as a ShellToolBand. \**************************************************************/ #include "priv.h" #include "sccls.h" #include "addrlist.h" #include "itbar.h" #include "itbdrop.h" #include "util.h" #include "aclhist.h" #include "aclmulti.h" #include "autocomp.h" #include "address.h" #include "shellurl.h" #include "resource.h" #include "uemapp.h" #include #include "apithk.h" #include "mluisupp.h" #define SUPERCLASS CToolBand #define MIN_DROPWIDTH 200 const static TCHAR c_szAddressBandProp[] = TEXT("CAddressBand_This"); //================================================================= // Implementation of CAddressBand //================================================================= //=========================== // *** IUnknown Interface *** HRESULT CAddressBand::QueryInterface(REFIID riid, void **ppvObj) { if (IsEqualIID(riid, IID_IWinEventHandler)) { *ppvObj = SAFECAST(this, IWinEventHandler*); } else if (IsEqualIID(riid, IID_IAddressBand)) { *ppvObj = SAFECAST(this, IAddressBand*); } else if (IsEqualIID(riid, IID_IPersistStream)) { *ppvObj = SAFECAST(this, IPersistStream*); } else if (IsEqualIID(riid, IID_IServiceProvider)) { *ppvObj = SAFECAST(this, IServiceProvider*); } else if (IsEqualIID(riid, IID_IInputObjectSite)) { *ppvObj = SAFECAST(this, IInputObjectSite*); } else { return SUPERCLASS::QueryInterface(riid, ppvObj); } AddRef(); return S_OK; } //================================ // *** IDockingWindow Interface *** /****************************************************\ FUNCTION: ShowDW DESCRIPTION: fShow == TRUE means show the window, FALSE means remove the window from the view. The window will be created if needed. \****************************************************/ HRESULT CAddressBand::ShowDW(BOOL fShow) { if (!_hwnd) return S_FALSE; // The window needs to be created first. ShowWindow(_hwnd, fShow ? SW_SHOW : SW_HIDE); // Refresh if we are becoming visible because we could have // received and ignored FileSysChange() events while // we where hidden. if (fShow && !_fVisible) Refresh(NULL); _fVisible = BOOLIFY(fShow); return SUPERCLASS::ShowDW(fShow); } HRESULT CAddressBand::CloseDW(DWORD dw) { if(_paeb) _paeb->Save(0); return SUPERCLASS::CloseDW(dw); } /****************************************************\ FUNCTION: SetSite DESCRIPTION: This function will be called to have this Toolband try to obtain enough information about its parent Toolbar to create the Band window and maybe connect to a Browser Window. \****************************************************/ HRESULT CAddressBand::SetSite(IUnknown *punkSite) { HRESULT hr; BOOL fSameHost = punkSite == _punkSite; if (!punkSite && _paeb) { IShellService * pss; hr = _paeb->QueryInterface(IID_IShellService, (LPVOID *)&pss); if (SUCCEEDED(hr)) { hr = pss->SetOwner(NULL); pss->Release(); } } hr = SUPERCLASS::SetSite(punkSite); if (punkSite && !fSameHost) { hr = _CreateAddressBand(punkSite); // This call failing is expected when the host doesn't have a Browser Window. } // Set or reset the AddressEditBox's Browser IUnknown. if (_paeb) { IShellService * pss; hr = _paeb->QueryInterface(IID_IShellService, (LPVOID *)&pss); if (SUCCEEDED(hr)) { // CAddressBand and the BandSite(host) have a ref count cycle. This cycle // is broken when BandSite calls SetSite(NULL) which will cause // CAddressBand to break the cycle by releasing it's punk to the BandSite. // // CAddressEditBox and CAddressBand have the same method of breaking the // cycle. This is accomplished by passing NULL to IAddressEditBox(NULL, NULL) // if our caller is breaking the cycle. This will cause CAddressEditBox to // release it's ref count on CAddressBand. hr = pss->SetOwner((punkSite ? SAFECAST(this, IAddressBand *) : NULL)); pss->Release(); } } // setsite must succeed return S_OK; } //================================ // *** IInputObject Methods *** HRESULT CAddressBand::TranslateAcceleratorIO(LPMSG lpMsg) { BOOL fForwardToView = FALSE; static CHAR szAccel[2] = "\0"; // Alt-D needs to be localizable switch (lpMsg->message) { case WM_KEYDOWN: // process these if (IsVK_TABCycler(lpMsg)) { // If we are tabbing away, let the edit box know so // that it clears its dirty flag. SendMessage(_hwndEdit, WM_KEYDOWN, VK_TAB, 0); } else { fForwardToView = TRUE; } switch (lpMsg->wParam) { case VK_F1: // help { // // FEATURE: Should add and accelerator for this and simply return S_FALSE, but that // causes two instances of the help dialog to come up when focus is in Trident. // This is the quick fix for IE5B2. // IOleCommandTarget* poct; IServiceProvider* psp; if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_IServiceProvider, (void**)&psp))) { if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser, IID_IOleCommandTarget, (LPVOID*)&poct))) { poct->Exec(&CGID_ShellBrowser, DVIDM_HELPSEARCH, 0, NULL, NULL); poct->Release(); } psp->Release(); } return S_FALSE; } case VK_F11: // fullscreen { return S_FALSE; } case VK_F4: { if (_fVisible) { if (HasFocusIO() == S_FALSE) SetFocus(_hwnd); // toggle the dropdown state SendMessage(_hwnd, CB_SHOWDROPDOWN, !SendMessage(_hwnd, CB_GETDROPPEDSTATE, 0, 0L), 0); // Leave focus in the edit box so you can keep typing if (_hwndEdit) SetFocus(_hwndEdit); } else { ASSERT(0); // Should this really be ignored? } return S_OK; } case VK_TAB: { // See if the editbox wants the tab character if (SendMessage(_hwndEdit, WM_GETDLGCODE, lpMsg->wParam, (LPARAM)lpMsg) == DLGC_WANTTAB) { // We want the tab character return S_OK; } break; } case VK_RETURN: { // // Ctrl-enter is used for quick complete, so pass it through // if (GetKeyState(VK_CONTROL) & 0x80000000) { TranslateMessage(lpMsg); DispatchMessage(lpMsg); return S_OK; } break; } } break; case WM_KEYUP: // eat any that WM_KEYDOWN processes switch (lpMsg->wParam) { case VK_F1: // help case VK_F11: // fullscreen return S_FALSE; case VK_RETURN: case VK_F4: case VK_TAB: return S_OK; default: break; } break; case WM_SYSCHAR: { CHAR szChar [2] = "\0"; if ('\0' == szAccel[0]) { MLLoadStringA(IDS_ADDRBAND_ACCELLERATOR,szAccel,ARRAYSIZE(szAccel)); } szChar[0] = (CHAR)lpMsg->wParam; if (lstrcmpiA(szChar,szAccel) == 0) { ASSERT(_fVisible); if (_fVisible && (HasFocusIO() == S_FALSE)) { SetFocus(_hwnd); } return S_OK; } } break; case WM_SYSKEYUP: // eat any that WM_SYSKEYDOWN processes if ('\0' == szAccel[0]) { MLLoadStringA(IDS_ADDRBAND_ACCELLERATOR,szAccel,ARRAYSIZE(szAccel)); } if ((CHAR)lpMsg->wParam == szAccel[0]) { return S_OK; } break; } HRESULT hres = EditBox_TranslateAcceleratorST(lpMsg); if (hres == S_FALSE && fForwardToView) { IShellBrowser *psb; // we did not process this try the view before we return if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_IShellBrowser, (void **)&psb))) { IShellView *psv; if (SUCCEEDED(psb->QueryActiveShellView(&psv))) { hres = psv->TranslateAccelerator(lpMsg); psv->Release(); } psb->Release(); } } return hres; } HRESULT CAddressBand::HasFocusIO() { if ((_hwndEdit&& (GetFocus() == _hwndEdit)) || SendMessage(_hwnd, CB_GETDROPPEDSTATE, 0, 0)) return S_OK; return S_FALSE; } //===================================== // *** IInputObjectSite Interface *** HRESULT CAddressBand::OnFocusChangeIS(IUnknown *punk, BOOL fSetFocus) { HRESULT hr; ASSERT(_punkSite); hr = IUnknown_OnFocusChangeIS(_punkSite, punk, fSetFocus); return hr; } //===================================== // *** IOleCommandTarget Interface *** HRESULT CAddressBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext) { ASSERT(_paeb); return IUnknown_QueryStatus(_paeb, pguidCmdGroup, cCmds, rgCmds, pcmdtext); } HRESULT CAddressBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut) { HRESULT hr = OLECMDERR_E_UNKNOWNGROUP; if (pguidCmdGroup == NULL) { // nothing } else if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup)) { switch (nCmdID) { case SBCMDID_GETADDRESSBARTEXT: hr = S_OK; TCHAR wz[MAX_URL_STRING]; UINT cb = 0; BSTR bstr = NULL; VariantInit(pvarargOut); if (_hwndEdit) cb = Edit_GetText(_hwndEdit, (TCHAR *)&wz, ARRAYSIZE(wz)); if (cb) bstr = SysAllocStringLen(NULL, cb); if (bstr) { SHTCharToUnicode(wz, bstr, cb); pvarargOut->vt = VT_BSTR|VT_BYREF; pvarargOut->byref = bstr; } else { // VariantInit() might do this for us. pvarargOut->vt = VT_EMPTY; pvarargOut->byref = NULL; return E_FAIL; // Edit_GetText gave us nothing } break; } } else if (IsEqualGUID(CGID_DeskBand, *pguidCmdGroup)) { switch (nCmdID) { case DBID_SETWINDOWTHEME: if (pvarargIn && pvarargIn->vt == VT_BSTR) { if (_hwnd) { Comctl32_SetWindowTheme(_hwnd, pvarargIn->bstrVal); Comctl32_SetWindowTheme(_hwndTools, pvarargIn->bstrVal); _BandInfoChanged(); } } hr = S_OK; break; } } if (FAILED(hr)) { hr = IUnknown_Exec(_paeb, pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); } return(hr); } extern HRESULT IsDesktopBrowser(IUnknown *punkSite); //================================ // *** IDeskBand Interface *** /****************************************************\ FUNCTION: GetBandInfo DESCRIPTION: This function will give the caller information about this Band, mainly the size of it. \****************************************************/ HRESULT CAddressBand::GetBandInfo(DWORD dwBandID, DWORD fViewMode, DESKBANDINFO* pdbi) { HRESULT hr = S_OK; _dwBandID = dwBandID; _fVertical = ((fViewMode & (DBIF_VIEWMODE_VERTICAL | DBIF_VIEWMODE_FLOATING)) != 0); pdbi->dwModeFlags = DBIMF_FIXEDBMP; pdbi->ptMinSize.x = 0; pdbi->ptMinSize.y = 0; if (_fVertical) { pdbi->ptMinSize.y = GetSystemMetrics(SM_CXSMICON); pdbi->ptMaxSize.y = -1; // random pdbi->ptIntegral.y = 1; pdbi->dwModeFlags |= DBIMF_VARIABLEHEIGHT; } else { if (_hwnd) { HWND hwndCombo; RECT rcCombo; hwndCombo = (HWND)SendMessage(_hwnd, CBEM_GETCOMBOCONTROL, 0, 0); ASSERT(hwndCombo); GetWindowRect(hwndCombo, &rcCombo); pdbi->ptMinSize.y = RECTHEIGHT(rcCombo); } ASSERT(pdbi->ptMinSize.y < 200); } MLLoadStringW(IDS_BAND_ADDRESS2, pdbi->wszTitle, ARRAYSIZE(pdbi->wszTitle)); if (IsDesktopBrowser(_punkSite) != S_FALSE) { // non- shell browser host (e.g. desktop or tray) // // this is slightly (o.k., very) hoaky. the only time we want to // show a mnemonic is when we're in a browser app. arguably we // should generalize this to all bands/bandsites by having a // DBIMF_WITHMNEMONIC or somesuch, but that would mean adding a // CBandSite::_dwModeFlag=0 and overriding it in itbar::CBandSite. // that seems like a lot of work for a special case so instead we // hack it in here based on knowledge of our host. TraceMsg(DM_TRACE, "cab.gbi: nuke Address mnemonic"); MLLoadStringW(IDS_BAND_ADDRESS, pdbi->wszTitle, ARRAYSIZE(pdbi->wszTitle)); } return hr; } //================================ // ** IWinEventHandler Interface *** /****************************************************\ FUNCTION: OnWinEvent DESCRIPTION: This function will give receive events from the parent ShellToolbar. \****************************************************/ HRESULT CAddressBand::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres) { switch (uMsg) { case WM_WININICHANGE: if (SHIsExplorerIniChange(wParam, lParam) & (EICH_KINET | EICH_KINETMAIN)) { _InitGoButton(); } if (wParam == SPI_SETNONCLIENTMETRICS) { // Tell the combobox so that it can update its font SendMessage(_hwnd, uMsg, wParam, lParam); // Inform the band site that our height may have changed _BandInfoChanged(); } break; case WM_COMMAND: { UINT idCmd = GET_WM_COMMAND_ID(wParam, lParam); if (idCmd == FCIDM_VIEWGOBUTTON) { // Toggle the go-button visibility BOOL fShowGoButton = !SHRegGetBoolUSValue(REGSTR_PATH_MAIN, TEXT("ShowGoButton"), FALSE, /*default*/TRUE); SHRegSetUSValue(REGSTR_PATH_MAIN, TEXT("ShowGoButton"), REG_SZ, (LPVOID)(fShowGoButton ? L"yes" : L"no"), (fShowGoButton ? 4 : 3)*sizeof(TCHAR), SHREGSET_FORCE_HKCU); // Tell the world that something has changed SendShellIEBroadcastMessage(WM_WININICHANGE, 0, (LPARAM)REGSTR_PATH_MAIN, 3000); } } } if (_pweh) return _pweh->OnWinEvent(_hwnd, uMsg, wParam, lParam, plres); else return S_OK; } /****************************************************\ FUNCTION: IsWindowOwner DESCRIPTION: This function will return TRUE if the HWND passed in is a HWND owned by this band. \****************************************************/ HRESULT CAddressBand::IsWindowOwner(HWND hwnd) { if (_pweh) return _pweh->IsWindowOwner(hwnd); else return S_FALSE; } //================================ // *** IAddressBand Interface *** /****************************************************\ FUNCTION: FileSysChange DESCRIPTION: This function will handle file system change notifications. \****************************************************/ HRESULT CAddressBand::FileSysChange(DWORD dwEvent, LPCITEMIDLIST * ppidl) { HRESULT hr = S_OK; if (_fVisible) { hr = IUnknown_FileSysChange(_paeb, dwEvent, ppidl); } return hr; } /****************************************************\ FUNCTION: Refresh PARAMETERS: pvarType - NULL for a refress of everything. OLECMD_REFRESH_TOPMOST will only update the top most. DESCRIPTION: This function will force a refress of part or all of the AddressBand. \****************************************************/ HRESULT CAddressBand::Refresh(VARIANT * pvarType) { HRESULT hr = S_OK; IAddressBand * pab; if (_paeb) { hr = _paeb->QueryInterface(IID_IAddressBand, (LPVOID *)&pab); if (SUCCEEDED(hr)) { hr = pab->Refresh(pvarType); pab->Release(); } } return hr; } /****************************************************\ Address Band Constructor \****************************************************/ CAddressBand::CAddressBand() { TraceMsg(TF_SHDLIFE, "ctor CAddressBand %x", this); // This needs to be allocated in Zero Inited Memory. // ASSERT that all Member Variables are inited to Zero. ASSERT(!_hwndEdit); ASSERT(!_paeb); ASSERT(!_pweh); _fCanFocus = TRUE; // we accept focus (see CToolBand::UIActivateIO) } /****************************************************\ Address Band destructor \****************************************************/ CAddressBand::~CAddressBand() { ATOMICRELEASE(_paeb); ATOMICRELEASE(_pweh); // // Make sure the toolbar is destroyed before we free // the image lists // if (_hwndTools && IsWindow(_hwndTools)) { DestroyWindow(_hwndTools); } if (_himlDefault) ImageList_Destroy(_himlDefault); if (_himlHot) ImageList_Destroy(_himlHot); // // Our window must be destroyed before we are freed // so that the window doesn't try to reference us. // if (_hwnd && IsWindow(_hwnd)) { DestroyWindow(_hwnd); // Null out base classes window handle because // its destructor is next _hwnd = NULL; } TraceMsg(TF_SHDLIFE, "dtor CAddressBand %x", this); } /****************************************************\ FUNCTION: CAddressBand_CreateInstance DESCRIPTION: This function will create an instance of the AddressBand COM object. \****************************************************/ HRESULT CAddressBand_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi) { // aggregation checking is handled in class factory *ppunk = NULL; CAddressBand * p = new CAddressBand(); if (p) { *ppunk = SAFECAST(p, IDeskBand *); return NOERROR; } return E_OUTOFMEMORY; } /****************************************************\ FUNCTION: _CreateAddressBand DESCRIPTION: This function will create the AddressBand window with the ComboBox. \****************************************************/ HRESULT CAddressBand::_CreateAddressBand(IUnknown * punkSite) { HRESULT hr = S_OK; if (_hwnd) { IShellService * pss; if (_hwndTools) { DestroyWindow(_hwndTools); _hwndTools = NULL; } DestroyWindow(_hwnd); _hwnd = NULL; ASSERT(_punkSite); if (_paeb) { hr = _paeb->QueryInterface(IID_IShellService, (LPVOID *)&pss); if (SUCCEEDED(hr)) { hr = pss->SetOwner(NULL); pss->Release(); } } ATOMICRELEASE(_paeb); ATOMICRELEASE(_pweh); } // // Create address window. // ASSERT(_hwndParent); // Call us after SetSite() if (!_hwndParent) { // The caller hasn't called SetSite(), so we can't // create our window because we can't find out our parent's // HWND. return E_FAIL; } _InitComCtl32(); // don't check result, if this fails our CreateWindows will fail DWORD dwWindowStyles = WS_TABSTOP | WS_CHILD | WS_CLIPCHILDREN | WS_TABSTOP | CBS_DROPDOWN | CBS_AUTOHSCROLL; // WARNING: MSN and other Rooted Explorers may not have implemented all // of the ParseDisplayName and other IShellFolder members // If we want to continue to support MSN, we will need to turn on the // CBS_DROPDOWNLIST if ISROOTEDCLASS() and the clsid is equal to the MSN clsid. // dwWindowStyles |= CBS_DROPDOWNLIST; // (This turns off the ComboBox's Editbox) DWORD dwExStyle = WS_EX_TOOLWINDOW; if (IS_WINDOW_RTL_MIRRORED(_hwndParent)) { // If the parent window is mirrored then the ComboBox window will inheret the mirroring flag // And we need the reading order to be Left to right, which is the right to left in the mirrored mode. dwExStyle |= WS_EX_RTLREADING; } _hwnd = CreateWindowEx(dwExStyle, WC_COMBOBOXEX, NULL, dwWindowStyles, 0, 0, 100, 250, _hwndParent, (HMENU) FCIDM_VIEWADDRESS, HINST_THISDLL, NULL); if (_hwnd) { // Initial combobox parameters. SendMessage(_hwnd, CBEM_SETEXTENDEDSTYLE, CBES_EX_NOSIZELIMIT | CBES_EX_CASESENSITIVE, CBES_EX_NOSIZELIMIT | CBES_EX_CASESENSITIVE); // NOTE: _hwndEdit will be NULL if the CBS_DROPDOWNLIST flag has been turned on _hwndEdit = (HWND)SendMessage(_hwnd, CBEM_GETEDITCONTROL, 0, 0L); _hwndCombo = (HWND)SendMessage(_hwnd, CBEM_GETCOMBOCONTROL, 0, 0L); // Subclass the Edit control's procedure to handle ModeBias issue. if ( _hwndEdit && SetProp(_hwndEdit, c_szAddressBandProp, this)) { _pfnOldEditProc = (WNDPROC) SetWindowLongPtr(_hwndEdit, GWLP_WNDPROC, (LONG_PTR) _ComboExEditProc); } ASSERT(!_paeb && !_pweh); hr = CoCreateInstance(CLSID_AddressEditBox, NULL, CLSCTX_INPROC_SERVER, IID_IAddressEditBox, (void **)&_paeb); // If this object fails to initialize, it won't work!!! Make sure you REGSVR32ed and RUNDLL32ed shdocvw.dll if (SUCCEEDED(hr)) { hr = _paeb->QueryInterface(IID_IWinEventHandler, (LPVOID *)&_pweh); ASSERT(SUCCEEDED(hr)); hr = _paeb->Init(_hwnd, _hwndEdit, AEB_INIT_AUTOEXEC, SAFECAST(this, IAddressBand *)); } // Create the go button if it's enabled _InitGoButton(); } else { hr = E_OUTOFMEMORY; } return hr; } //================================ // *** IPersistStream Interface *** /****************************************************\ FUNCTION: Load DESCRIPTION: This function will currently only persist the CAddressEditBox object. HISTORY: Ver 1: Contains the CAddressEditBox::Save() stream. \****************************************************/ #define STREAM_VERSION_CADDRESSBAND 0x00000001 HRESULT CAddressBand::Load(IStream *pstm) { HRESULT hr; DWORD dwSize; DWORD dwVersion; hr = LoadStreamHeader(pstm, STREAMHEADER_SIG_CADDRESSBAND, STREAM_VERSION_CADDRESSBAND, STREAM_VERSION_CADDRESSBAND, &dwSize, &dwVersion); ASSERT(SUCCEEDED(hr)); if (S_OK == hr) { switch (dwVersion) { case 1: // Ver 1. // Nothing. break; default: ASSERT(0); // Should never get here. break; } } else if (S_FALSE == hr) hr = S_OK; // We already have our default data set. return hr; } /****************************************************\ FUNCTION: Save DESCRIPTION: This function will currently only persist the CAddressEditBox object. HISTORY: Ver 1: Contains the CAddressEditBox::Save() stream. \****************************************************/ HRESULT CAddressBand::Save(IStream *pstm, BOOL fClearDirty) { HRESULT hr; hr = SaveStreamHeader(pstm, STREAMHEADER_SIG_CADDRESSBAND, STREAM_VERSION_CADDRESSBAND, 0); ASSERT(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { IPersistStream * pps; ASSERT(_paeb); if (_paeb) { hr = _paeb->QueryInterface(IID_IPersistStream, (LPVOID *)&pps); if(EVAL(SUCCEEDED(hr))) { hr = pps->Save(pstm, fClearDirty); pps->Release(); } } } return hr; } void CAddressBand::_OnGetInfoTip(LPNMTBGETINFOTIP pnmTT) { // Format a tooltip: "go to " WCHAR szAddress[MAX_PATH]; if (GetWindowText(_hwndEdit, szAddress, ARRAYSIZE(szAddress))) { WCHAR szFormat[MAX_PATH]; const int MAX_TOOLTIP_LENGTH = 100; int cchMax = (pnmTT->cchTextMax < MAX_TOOLTIP_LENGTH) ? pnmTT->cchTextMax : MAX_TOOLTIP_LENGTH; MLLoadString(IDS_GO_TOOLTIP, szFormat, ARRAYSIZE(szFormat)); int cch; if(SUCCEEDED(StringCchPrintf(pnmTT->pszText, cchMax, szFormat, szAddress))) { cch = lstrlen(pnmTT->pszText); // Append ellipses? if (cch == cchMax - 1) { // Note that Japan has a single character for ellipses, so we load // as a resource. WCHAR szEllipses[10]; cch = MLLoadString(IDS_ELLIPSES, szEllipses, ARRAYSIZE(szEllipses)); StringCchCopy(pnmTT->pszText + cchMax - cch - 1, cch + 1, szEllipses); } } } else if (pnmTT->cchTextMax > 0) { // Use button text for tooltip *pnmTT->pszText = L'\0'; } } //+------------------------------------------------------------------------- // Subclassed window procedure of the combobox Edit control in the address band //-------------------------------------------------------------------------- LRESULT CALLBACK CAddressBand::_ComboExEditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CAddressBand* pThis = (CAddressBand*)GetProp(hwnd, c_szAddressBandProp); if (!pThis) return DefWindowProcWrap(hwnd, uMsg, wParam, lParam); WNDPROC pfnOldEditProc = pThis->_pfnOldEditProc; switch (uMsg) { case WM_KILLFOCUS : SetModeBias(MODEBIASMODE_DEFAULT); break; case WM_SETFOCUS: SetModeBias(MODEBIASMODE_URLHISTORY); break; case WM_DESTROY: // // Unsubclass myself. // RemoveProp(hwnd, c_szAddressBandProp); if (pfnOldEditProc) { SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) pfnOldEditProc); pThis->_pfnOldEditProc = NULL; } break; default: break; } return CallWindowProc(pfnOldEditProc, hwnd, uMsg, wParam, lParam); } //+------------------------------------------------------------------------- // Subclassed window procedure of the combobox in the address band //-------------------------------------------------------------------------- LRESULT CALLBACK CAddressBand::_ComboExWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CAddressBand* pThis = (CAddressBand*)GetProp(hwnd, c_szAddressBandProp); if (!pThis) return DefWindowProcWrap(hwnd, uMsg, wParam, lParam); WNDPROC pfnOldWndProc = pThis->_pfnOldWndProc; switch (uMsg) { case WM_NOTIFYFORMAT: if (NF_QUERY == lParam) { return (DLL_IS_UNICODE ? NFR_UNICODE : NFR_ANSI); } break; case WM_WINDOWPOSCHANGING: { // Break out if the go button is hidden if (!pThis->_fGoButton) break; // // Make room for the go button on the right side // LPWINDOWPOS pwp = (LPWINDOWPOS)lParam; pwp->flags |= SWP_NOCOPYBITS; WINDOWPOS wp = *(LPWINDOWPOS)lParam; // Get the dimensions of our 'go' button RECT rc; SendMessage(pThis->_hwndTools, TB_GETITEMRECT, 0, (LPARAM)&rc); int cxGo = RECTWIDTH(rc); int cyGo = RECTHEIGHT(rc); // Make room for the go button on the right side wp.cx -= cxGo + 2; CallWindowProc(pfnOldWndProc, hwnd, uMsg, wParam, (LPARAM)&wp); // Paint underneath the 'go' button RECT rcGo = {wp.cx, 0, wp.cx + cxGo + 2, wp.cy}; InvalidateRect(pThis->_hwnd, &rcGo, TRUE); // The outer window can be much higher than the internal combobox. // We want to center the go button on the combobox int y; if (pThis->_hwndCombo) { // Center vertically with inner combobox RECT rcCombo; GetWindowRect(pThis->_hwndCombo, &rcCombo); y = (rcCombo.bottom - rcCombo.top - cyGo)/2; } else { y = (wp.cy - cyGo)/2; } // Position the 'go' button on the right. Note that the height will always be ok // because the addressbar displays 16x16 icons within it. SetWindowPos(pThis->_hwndTools, NULL, wp.cx + 2, y, cxGo, cyGo, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOOWNERZORDER); // Adjust the drop-down width SendMessage(pThis->_hwndCombo, CB_SETDROPPEDWIDTH, MIN_DROPWIDTH, 0L); return 0; } case WM_SIZE: { // Break out if the go button is hidden if (!pThis->_fGoButton) break; // // Make room for the go button on the right side // int cx = LOWORD(lParam); int cy = HIWORD(lParam); // Get the dimensions of our 'go' button RECT rc; SendMessage(pThis->_hwndTools, TB_GETITEMRECT, 0, (LPARAM)&rc); int cxGo = RECTWIDTH(rc); int cyGo = RECTHEIGHT(rc); // Make room for the go button on the right side LPARAM lParamTemp = MAKELONG(cx - cxGo - 2, cy); CallWindowProc(pfnOldWndProc, hwnd, uMsg, wParam, lParamTemp); // Paint underneath the 'go' button RECT rcGo = {cx-cxGo, 0, cx, cy}; InvalidateRect(pThis->_hwnd, &rcGo, TRUE); // The outer window can be much higher than the internal combobox. // We want to center the go button on the combobox int y; if (pThis->_hwndCombo) { // Center vertically with inner combobox RECT rcCombo; GetWindowRect(pThis->_hwndCombo, &rcCombo); y = (rcCombo.bottom - rcCombo.top - cyGo)/2; } else { y = (cy - cyGo)/2; } // Position the 'go' button on the right. Note that the height will always be ok // because the addressbar displays 16x16 icons within it. SetWindowPos(pThis->_hwndTools, NULL, cx - cxGo, y, cxGo, cyGo, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOOWNERZORDER); // Adjust the drop-down width SendMessage(pThis->_hwndCombo, CB_SETDROPPEDWIDTH, MIN_DROPWIDTH, 0L); return 0; } case WM_NOTIFY: { LPNMHDR pnm = (LPNMHDR)lParam; if (pnm->hwndFrom == pThis->_hwndTools) { switch (pnm->code) { case NM_CLICK: // Simulate an enter key press in the combobox SendMessage(pThis->_hwndEdit, WM_KEYDOWN, VK_RETURN, 0); SendMessage(pThis->_hwndEdit, WM_KEYUP, VK_RETURN, 0); // n.b. we also got a NAVADDRESS from the simulate UEMFireEvent(&UEMIID_BROWSER, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_NAVIGATE, UIBL_NAVGO); break; case NM_TOOLTIPSCREATED: { // // Make the tooltip show up even when the app is nit active // NMTOOLTIPSCREATED* pnmTTC = (NMTOOLTIPSCREATED*)pnm; SHSetWindowBits(pnmTTC->hwndToolTips, GWL_STYLE, TTS_ALWAYSTIP | TTS_TOPMOST | TTS_NOPREFIX, TTS_ALWAYSTIP | TTS_TOPMOST | TTS_NOPREFIX); } break; case TBN_GETINFOTIP: pThis->_OnGetInfoTip((LPNMTBGETINFOTIP)pnm); break; } return 0; } break; } case WM_ERASEBKGND: { // Break out if the go button is hidden if (!pThis->_fGoButton) break; // // Forward the erase background to the parent so that // we appear transparent under the go button // HDC hdc = (HDC)wParam; HWND hwndParent = GetParent(hwnd); LRESULT lres = 0; if (hwndParent) { // Adjust the origin so the parent paints in the right place POINT pt = {0,0}; MapWindowPoints(hwnd, hwndParent, &pt, 1); OffsetWindowOrgEx(hdc, pt.x, pt.y, &pt); lres = SendMessage(hwndParent, WM_ERASEBKGND, (WPARAM)hdc, 0L); SetWindowOrgEx(hdc, pt.x, pt.y, NULL); } if (lres != 0) { // We handled it return lres; } break; } case WM_DESTROY: // // Unsubclass myself. // RemoveProp(hwnd, c_szAddressBandProp); if (pfnOldWndProc) { SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) pfnOldWndProc); pThis->_pfnOldWndProc = NULL; } break; default: break; } return CallWindowProc(pfnOldWndProc, hwnd, uMsg, wParam, lParam); } //+------------------------------------------------------------------------- // Creates and shows the go button //-------------------------------------------------------------------------- BOOL CAddressBand::_CreateGoButton() { ASSERT(_hwndTools == NULL); BOOL fRet = FALSE; BOOL bUseClassicGlyphs = SHUseClassicToolbarGlyphs(); COLORREF crMask = RGB(255, 0, 255); if (_himlDefault == NULL) { if (bUseClassicGlyphs) { _himlDefault = ImageList_LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDB_GO), 16, 0, crMask, IMAGE_BITMAP, LR_CREATEDIBSECTION); } else { _himlDefault = ImageList_LoadImage(GetModuleHandle(TEXT("shell32.dll")), MAKEINTRESOURCE(IDB_TB_GO_DEF_20), 20, 0, crMask, IMAGE_BITMAP, LR_CREATEDIBSECTION); } } if (_himlHot == NULL) { if (bUseClassicGlyphs) { _himlHot = ImageList_LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDB_GOHOT), 16, 0, crMask, IMAGE_BITMAP, LR_CREATEDIBSECTION); } else { _himlHot = ImageList_LoadImage(GetModuleHandle(TEXT("shell32.dll")), MAKEINTRESOURCE(IDB_TB_GO_HOT_20), 20, 0, crMask, IMAGE_BITMAP, LR_CREATEDIBSECTION); } } // If we have the image lists, go ahead and create the toolbar control for the go button if (_himlDefault && _himlHot) { // // Subclass the comboboxex so that we can place the go botton within it. The toolbad class // assumes one window per band, so this trick allows us to add the button using existing windows. // Note that comboex controls have a separate window used to wrap the internal combobox. This // is the window that we use to host our "go" button. We must subclass before creating the // go button so that we respond to WM_NOTIFYFORMAT with NFR_UNICODE. // // if (SetProp(_hwnd, c_szAddressBandProp, this)) { _pfnOldWndProc = (WNDPROC) SetWindowLongPtr(_hwnd, GWLP_WNDPROC, (LONG_PTR) _ComboExWndProc); } // Create the toolbar control for the go button _hwndTools = CreateWindowEx(WS_EX_TOOLWINDOW, TOOLBARCLASSNAME, NULL, WS_CHILD | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_LIST | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE, 0, 0, 0, 0, _hwnd, NULL, HINST_THISDLL, NULL); } if (_hwndTools) { // Init the toolbar control SendMessage(_hwndTools, TB_BUTTONSTRUCTSIZE, SIZEOF(TBBUTTON), 0); SendMessage(_hwndTools, TB_SETMAXTEXTROWS, 1, 0L); SendMessage(_hwndTools, TB_SETBUTTONWIDTH, 0, (LPARAM) MAKELONG(0, 500)); SendMessage(_hwndTools, TB_SETIMAGELIST, 0, (LPARAM)_himlDefault); SendMessage(_hwndTools, TB_SETHOTIMAGELIST, 0, (LPARAM)_himlHot); LRESULT nRet = SendMessage(_hwndTools, TB_ADDSTRING, (WPARAM)MLGetHinst(), (LPARAM)IDS_ADDRESS_TB_LABELS); ASSERT(nRet == 0); static const TBBUTTON tbb[] = { {0, 1, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 0}, }; SendMessage(_hwndTools, TB_ADDBUTTONS, ARRAYSIZE(tbb), (LPARAM)tbb); fRet = TRUE; } else { // If no toolbar control, don't subclass the comboboxex if (_pfnOldWndProc) { RemoveProp(_hwnd, c_szAddressBandProp); SetWindowLongPtr(_hwnd, GWLP_WNDPROC, (LONG_PTR) _pfnOldWndProc); _pfnOldWndProc = NULL; } } return fRet; } //+------------------------------------------------------------------------- // Shows/hides the go button depending on the current registry settings //-------------------------------------------------------------------------- void CAddressBand::_InitGoButton() { BOOL fUpdate = FALSE; // // Create the go button if it's enabled // // down-level client fix: only show Go in shell areas when NT5 or greater // or on a window that was originally IE BOOL fShowGoButton = SHRegGetBoolUSValue(REGSTR_PATH_MAIN, TEXT("ShowGoButton"), FALSE, /*default*/TRUE) && (WasOpenedAsBrowser(_punkSite) || GetUIVersion() >= 5); if (fShowGoButton && (_hwndTools || _CreateGoButton())) { ShowWindow(_hwndTools, SW_SHOW); _fGoButton = TRUE; fUpdate = TRUE; } else if (_hwndTools && IsWindowVisible(_hwndTools)) { ShowWindow(_hwndTools, SW_HIDE); _fGoButton = FALSE; fUpdate = TRUE; } // If the go button was hidden or shown, get the combobox to adjust itself if (fUpdate) { // Resetting the item height gets the combobox to update the size of the editbox LRESULT iHeight = SendMessage(_hwnd, CB_GETITEMHEIGHT, -1, 0); if (iHeight != CB_ERR) { SendMessage(_hwnd, CB_SETITEMHEIGHT, -1, iHeight); } } }