Leaked source code of windows server 2003
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.

2945 lines
97 KiB

  1. /**************************************************************\
  2. FILE: aeditbox.cpp
  3. DESCRIPTION:
  4. The Class CAddressEditBox exists to support a typical
  5. set of functionality used in Editboxes or ComboBoxes. This
  6. object will add AutoComplete functionality to the EditBox and
  7. specify default "AutoComplete" Lists. If the control is a
  8. ComboBoxEd, this object will populate the drop down list
  9. appropriately.
  10. \**************************************************************/
  11. #include "priv.h"
  12. #include "sccls.h"
  13. #include "addrlist.h"
  14. #include "itbar.h"
  15. #include "itbdrop.h"
  16. #include "util.h"
  17. #include "aclhist.h"
  18. #include "aclmulti.h"
  19. #include "autocomp.h"
  20. #include "address.h"
  21. #include "shellurl.h"
  22. #include "bandprxy.h"
  23. #include "uemapp.h"
  24. #include "apithk.h"
  25. #include "accdel.h"
  26. #include "resource.h"
  27. #include "mluisupp.h"
  28. extern DWORD g_dwStopWatchMode;
  29. // Needed in order to track down NTRAID#187504-Bryanst-Tracking Winsta for corruption
  30. HWINSTA g_hWinStationBefore = NULL;
  31. HWINSTA g_hWinStationAfter = NULL;
  32. HWINSTA g_hWinStationAfterEx = NULL;
  33. // Internal message for async processing of the IDList when navigating
  34. UINT g_nAEB_AsyncNavigation = 0;
  35. ///////////////////////////////////////////////////////////////////
  36. // #DEFINEs
  37. #define SZ_ADDRESSCOMBO_PROP TEXT("CAddressCombo_This")
  38. #define SZ_ADDRESSCOMBOEX_PROP TEXT("CAddressComboEx_This")
  39. #define SEL_ESCAPE_PRESSED (-2)
  40. ///////////////////////////////////////////////////////////////////
  41. // Data Structures
  42. enum ENUMLISTTYPE
  43. {
  44. LT_NONE,
  45. LT_SHELLNAMESPACE,
  46. LT_TYPEIN_MRU,
  47. };
  48. ///////////////////////////////////////////////////////////////////
  49. // class AsyncNav: this object contains all the necessary information
  50. // to execute an asynchronous navigation task, so that
  51. // the user doesn't have to wait for navigation to
  52. // finish before doing anything, and the navigation
  53. // can be canceled if it takes too long.
  54. class AsyncNav
  55. {
  56. public:
  57. // Public Functions ***************************************
  58. AsyncNav()
  59. {
  60. _cRef = 1;
  61. _pShellUrl = NULL;
  62. _pszUrl = NULL;
  63. }
  64. LONG AddRef()
  65. {
  66. return InterlockedIncrement(&_cRef);
  67. }
  68. LONG Release()
  69. {
  70. ASSERT( 0 != _cRef );
  71. LONG cRef = InterlockedDecrement(&_cRef);
  72. if ( 0 == cRef )
  73. {
  74. delete this;
  75. }
  76. return cRef;
  77. }
  78. void SetCanceledFlag() {_fWasCanceled = TRUE;}
  79. // Data members ***************************************
  80. CShellUrl * _pShellUrl;
  81. DWORD _dwParseFlags;
  82. BOOL _fWasCorrected;
  83. BOOL _fPidlCheckOnly;
  84. HRESULT _hr;
  85. LPTSTR _pszUrl;
  86. BOOL _fWasCanceled;
  87. HWND _hwnd; // HWND that receives the message when processing is done.
  88. BOOL _fReady; // This ensures that we will not try to use the object before it's ready
  89. // CONSIDER: the memory can be released and then re-used by the same object
  90. // CONSIDER: which would have us believe that the navigation should be done.
  91. // CONSIDER: But if the navigation had been canceled and the memory re-used by the next AsyncNav alloc
  92. // CONSIDER: we would handle the message g_nAEB_AsyncNavigation with an
  93. // CONSIDER: unprocessed AsyncNav object. (See the handler for g_nAEB_AsyncNavigation).
  94. private:
  95. LONG _cRef;
  96. ~AsyncNav()
  97. {
  98. delete _pShellUrl;
  99. _pShellUrl = NULL;
  100. Str_SetPtr(&_pszUrl, NULL);
  101. }
  102. };
  103. ///////////////////////////////////////////////////////////////////
  104. // Prototypes
  105. /**************************************************************\
  106. CLASS: CAddressEditBox
  107. \**************************************************************/
  108. class CAddressEditBox
  109. : public IWinEventHandler
  110. , public IDispatch
  111. , public IAddressBand
  112. , public IAddressEditBox
  113. , public IOleCommandTarget
  114. , public IPersistStream
  115. , public IShellService
  116. {
  117. public:
  118. //////////////////////////////////////////////////////
  119. // Public Interfaces
  120. //////////////////////////////////////////////////////
  121. // *** IUnknown ***
  122. virtual STDMETHODIMP_(ULONG) AddRef(void);
  123. virtual STDMETHODIMP_(ULONG) Release(void);
  124. virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  125. // *** IOleCommandTarget methods ***
  126. virtual STDMETHODIMP QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds,
  127. OLECMD rgCmds[], OLECMDTEXT *pcmdtext);
  128. virtual STDMETHODIMP Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt,
  129. VARIANTARG *pvarargIn, VARIANTARG *pvarargOut);
  130. // *** IWinEventHandler methods ***
  131. virtual STDMETHODIMP OnWinEvent (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plre);
  132. virtual STDMETHODIMP IsWindowOwner(HWND hwnd);
  133. // *** IDispatch methods ***
  134. virtual STDMETHODIMP GetTypeInfoCount(UINT *pctinfo) {return E_NOTIMPL;}
  135. virtual STDMETHODIMP GetTypeInfo(UINT itinfo,LCID lcid,ITypeInfo **pptinfo) {return E_NOTIMPL;}
  136. virtual STDMETHODIMP GetIDsOfNames(REFIID riid,OLECHAR **rgszNames,UINT cNames, LCID lcid, DISPID * rgdispid) {return E_NOTIMPL;}
  137. virtual STDMETHODIMP Invoke(DISPID dispidMember,REFIID riid,LCID lcid,WORD wFlags,
  138. DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo,UINT * puArgErr);
  139. // *** IPersistStream methods ***
  140. virtual STDMETHODIMP GetClassID(CLSID *pClassID){ *pClassID = CLSID_AddressEditBox; return S_OK; }
  141. virtual STDMETHODIMP Load(IStream *pStm) {return S_OK;}
  142. virtual STDMETHODIMP Save(IStream *pStm, BOOL fClearDirty) { Save(0); return S_OK;}
  143. virtual STDMETHODIMP IsDirty(void) {return S_OK;} // Indicate that we are dirty and ::Save() needs to be called.
  144. virtual STDMETHODIMP GetSizeMax(ULARGE_INTEGER *pcbSize) {return E_NOTIMPL;}
  145. // *** IAddressBand methods ***
  146. virtual STDMETHODIMP FileSysChange(DWORD dwEvent, LPCITEMIDLIST *ppidl);
  147. virtual STDMETHODIMP Refresh(VARIANT * pvarType);
  148. // *** IAddressEditBox methods ***
  149. virtual STDMETHODIMP Init(HWND hwndComboBox, HWND hwndEditBox, DWORD dwFlags, IUnknown * punkParent);
  150. virtual STDMETHODIMP SetCurrentDir(LPCOLESTR pwzDir);
  151. virtual STDMETHODIMP ParseNow(DWORD dwFlags);
  152. virtual STDMETHODIMP Execute(DWORD dwExecFlags);
  153. virtual STDMETHODIMP Save(DWORD dwReserved);
  154. // *** IShellService methods ***
  155. STDMETHODIMP SetOwner(IUnknown* punkOwner);
  156. protected:
  157. //////////////////////////////////////////////////////
  158. // Private Member Functions
  159. //////////////////////////////////////////////////////
  160. // Constructor / Destructor
  161. CAddressEditBox();
  162. ~CAddressEditBox(void); // This is now an OLE Object and cannot be used as a normal Class.
  163. LRESULT _OnNotify(LPNMHDR pnm);
  164. LRESULT _OnCommand(WPARAM wParam, LPARAM lParam);
  165. LRESULT _OnBeginEdit(LPNMHDR pnm) ;
  166. LRESULT _OnEndEditW(LPNMCBEENDEDITW pnmW);
  167. LRESULT _OnEndEditA(LPNMCBEENDEDITA pnmA);
  168. HRESULT _ConnectToBrwsrConnectionPoint(BOOL fConnect, IUnknown * punk);
  169. HRESULT _ConnectToBrwsrWnd(IUnknown* punk);
  170. HRESULT _UseNewList(ENUMLISTTYPE eltNew);
  171. HRESULT _CreateCShellUrl(void);
  172. HRESULT _HandleUserAction(LPCTSTR pszUrl, int iNewSelection);
  173. HRESULT _NavigationComplete(LPCTSTR pszUrl, BOOL fChangeLists, BOOL fAddToMRU);
  174. void _SetAutocompleteOptions();
  175. void _GetUrlAndCache(void);
  176. BOOL _IsShellUrl(void);
  177. static HRESULT _NavigateToUrlCB(LPARAM lParam, LPTSTR lpUrl);
  178. static LRESULT CALLBACK _ComboSubclassWndProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
  179. // Functions for keeping dirty contents from getting clobbered
  180. BOOL _IsDirty();
  181. void _ClearDirtyFlag();
  182. void _InstallHookIfDirty();
  183. void _RemoveHook();
  184. LRESULT _MsgHook(int nCode, WPARAM wParam, MOUSEHOOKSTRUCT *pmhs);
  185. static LRESULT CALLBACK CAddressEditBox::_MsgHook(int nCode, WPARAM wParam, LPARAM lParam);
  186. static LRESULT CALLBACK _ComboExSubclassWndProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
  187. static LRESULT CALLBACK _EditSubclassWndProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
  188. static BOOL CALLBACK _EnumFindWindow(HWND hwnd, LPARAM lParam);
  189. HRESULT _FinishNavigate();
  190. static DWORD WINAPI _AsyncNavigateThreadProc(LPVOID pvData); // do async navigation: figure out the PIDL on a separate thread.
  191. void _JustifyAddressBarText( void );
  192. HRESULT _AsyncNavigate(AsyncNav *pAsyncNav);
  193. HRESULT _CancelNavigation();
  194. // Friend Functions
  195. friend HRESULT CAddressEditBox_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi);
  196. //////////////////////////////////////////////////////
  197. // Private Member Variables
  198. //////////////////////////////////////////////////////
  199. int m_cRef; // COM Object Ref Count
  200. IUnknown * m_punkParent; // Our Parent that will receive events if something happens.
  201. DWORD m_dwFlags; // Flags that will modify the behavior of this object.
  202. HWND m_hwnd; // Address ComboBoxEx Control if we control a ComboBoxEx.
  203. HWND m_hwndEdit; // Address EditBox Control Window
  204. WNDPROC m_lpfnComboWndProc; // Former WndProc of Combo child
  205. int m_nOldSelection; // Previous Drop Down Selection.
  206. // Objects for Navigation
  207. IBandProxy * m_pbp; // The BandProxy that will take care of finding the window to Navigate.
  208. IBrowserService*m_pbs; // Only valid when we are in a Browser Windows Toolbar. (Not Toolband)
  209. DWORD m_dwcpCookie; // ConnectionPoint cookie for DWebBrowserEvents2 from the Browser Window.
  210. LPTSTR m_pszCurrentUrl; // Needed in case refresh occurs.
  211. LPTSTR m_pszPendingURL; // Pending URL. We hang on to it until navigation finished before adding to MRU.
  212. LPTSTR m_pszUserEnteredURL; // Keep the exact text the user entered just in case we need to do a search.
  213. LPTSTR m_pszHttpErrorUrl;
  214. BOOL m_fDidShellExec; // Was the last navigation handled by calleding ShellExec()? (Used when refreshing)
  215. BOOL m_fConnectedToBrowser; // Are we connected to a browser?
  216. AsyncNav * m_pAsyncNav;
  217. // AutoComplete Functionality
  218. IAutoComplete2* m_pac; // AutoComplete object
  219. IShellService * m_pssACLISF; // AutoComplete ISF List. Needed if we need to change browsers.
  220. // AddressLists
  221. ENUMLISTTYPE m_elt;
  222. ENUMLISTTYPE m_eltPrevious;
  223. IAddressList * m_palCurrent; // CurrentList.
  224. IAddressList * m_palSNS; // Shell Name Space List.
  225. IAddressList * m_palMRU; // Type-in MRU List.
  226. IMRU * m_pmru; // MRU List.
  227. CShellUrl * m_pshuUrl;
  228. // Variables for keeping dirty contens from getting clobbered
  229. static CAssociationList m_al; // associate thread id with this class
  230. WNDPROC m_lpfnComboExWndProc;// Former WndProc of ComboBoxEx
  231. WNDPROC m_lpfnEditWndProc; // Former WndProc of Edit control in ComboBox
  232. HHOOK m_hhook; // mouse message hook
  233. COMBOBOXEXITEM m_cbex; // last change received while dirty
  234. HWND m_hwndBrowser; // top-level browser window
  235. BOOL m_fAssociated; // if we are entered in m_al for this thread
  236. BOOL m_fAsyncNavInProgress; // tells if we have a pending async navigate already in progress
  237. };
  238. class CAddressEditAccessible : public CDelegateAccessibleImpl
  239. {
  240. public:
  241. CAddressEditAccessible(HWND hwndCombo, HWND hwndEdit);
  242. // *** IUnknown ***
  243. STDMETHODIMP_(ULONG) AddRef();
  244. STDMETHODIMP_(ULONG) Release();
  245. STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  246. // *** IAccessible ***
  247. STDMETHODIMP get_accName(VARIANT varChild, BSTR *pszName);
  248. STDMETHODIMP get_accValue(VARIANT varChild, BSTR *pszValue);
  249. protected:
  250. virtual ~CAddressEditAccessible();
  251. private:
  252. LONG m_cRefCount;
  253. HWND m_hwndEdit;
  254. LPWSTR m_pwszName;
  255. };
  256. //=================================================================
  257. // Static variables
  258. //=================================================================
  259. CAssociationList CAddressEditBox::m_al;
  260. //=================================================================
  261. // Implementation of CAddressEditBox
  262. //=================================================================
  263. //===========================
  264. // *** IUnknown Interface ***
  265. HRESULT CAddressEditBox::QueryInterface(REFIID riid, void **ppvObj)
  266. {
  267. if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IWinEventHandler))
  268. {
  269. *ppvObj = SAFECAST(this, IWinEventHandler*);
  270. }
  271. else if (IsEqualIID(riid, IID_IDispatch))
  272. {
  273. *ppvObj = SAFECAST(this, IDispatch*);
  274. }
  275. else if (IsEqualIID(riid, IID_IAddressBand))
  276. {
  277. *ppvObj = SAFECAST(this, IAddressBand*);
  278. }
  279. else if (IsEqualIID(riid, IID_IAddressEditBox))
  280. {
  281. *ppvObj = SAFECAST(this, IAddressEditBox*);
  282. }
  283. else if (IsEqualIID(riid, IID_IOleCommandTarget))
  284. {
  285. *ppvObj = SAFECAST(this, IOleCommandTarget*);
  286. }
  287. else if (IsEqualIID(riid, IID_IPersistStream))
  288. {
  289. *ppvObj = SAFECAST(this, IPersistStream*);
  290. }
  291. else if (IsEqualIID(riid, IID_IShellService))
  292. {
  293. *ppvObj = SAFECAST(this, IShellService*);
  294. }
  295. else
  296. {
  297. *ppvObj = NULL;
  298. return E_NOINTERFACE;
  299. }
  300. AddRef();
  301. return S_OK;
  302. }
  303. ULONG CAddressEditBox::AddRef(void)
  304. {
  305. m_cRef++;
  306. return m_cRef;
  307. }
  308. ULONG CAddressEditBox::Release(void)
  309. {
  310. ASSERT(m_cRef > 0);
  311. m_cRef--;
  312. if (m_cRef > 0)
  313. {
  314. return m_cRef;
  315. }
  316. delete this;
  317. return 0;
  318. }
  319. //=====================================
  320. // *** IOleCommandTarget Interface ***
  321. HRESULT CAddressEditBox::QueryStatus(const GUID *pguidCmdGroup,
  322. ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
  323. {
  324. HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
  325. if (rgCmds == NULL)
  326. {
  327. return(E_INVALIDARG);
  328. }
  329. if (pguidCmdGroup==NULL)
  330. {
  331. hr = S_OK;
  332. for (UINT i=0; i<cCmds; i++)
  333. {
  334. ULONG l;
  335. rgCmds[i].cmdf = 0;
  336. switch (rgCmds[i].cmdID)
  337. {
  338. case OLECMDID_PASTE:
  339. if (m_hwndEdit && OpenClipboard(m_hwndEdit))
  340. {
  341. // IDEA: We might want to support CF_URL here (SatoNa)
  342. if (GetClipboardData(CF_TEXT))
  343. {
  344. rgCmds[i].cmdf = OLECMDF_ENABLED;
  345. }
  346. CloseClipboard();
  347. }
  348. break;
  349. case OLECMDID_COPY:
  350. case OLECMDID_CUT:
  351. if (m_hwndEdit)
  352. {
  353. l=(ULONG)SendMessage(m_hwndEdit, EM_GETSEL, 0, 0);
  354. if (LOWORD(l) != HIWORD(l))
  355. {
  356. rgCmds[i].cmdf = OLECMDF_ENABLED;
  357. }
  358. }
  359. break;
  360. case OLECMDID_SELECTALL:
  361. if (m_hwndEdit)
  362. {
  363. // Select All -- not allowed if there's no text or if everything is
  364. // selected. Latter case takes care of first one.
  365. int ichMinSel;
  366. int ichMaxSel;
  367. int cch = (int)SendMessage(m_hwndEdit, WM_GETTEXTLENGTH, 0, 0);
  368. SendMessage(m_hwndEdit, EM_GETSEL, (WPARAM)&ichMinSel, (LPARAM)&ichMaxSel);
  369. if ((ichMinSel != 0) || (ichMaxSel != cch))
  370. {
  371. rgCmds[i].cmdf = OLECMDF_ENABLED;
  372. }
  373. }
  374. }
  375. }
  376. }
  377. return(hr);
  378. }
  379. HRESULT CAddressEditBox::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt,
  380. VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  381. {
  382. HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
  383. if (pguidCmdGroup == NULL)
  384. {
  385. hr = S_OK;
  386. switch(nCmdID)
  387. {
  388. case OLECMDID_COPY:
  389. if (m_hwndEdit)
  390. SendMessage(m_hwndEdit, WM_COPY, 0, 0);
  391. break;
  392. case OLECMDID_PASTE:
  393. // IDEA: We might want to support CF_URL here (SatoNa)
  394. if (m_hwndEdit)
  395. SendMessage(m_hwndEdit, WM_PASTE, 0, 0);
  396. break;
  397. case OLECMDID_CUT:
  398. if (m_hwndEdit)
  399. SendMessage(m_hwndEdit, WM_CUT, 0, 0);
  400. break;
  401. case OLECMDID_SELECTALL:
  402. if (m_hwndEdit)
  403. Edit_SetSel(m_hwndEdit, 0, (LPARAM)-1);
  404. break;
  405. default:
  406. hr = OLECMDERR_E_UNKNOWNGROUP;
  407. break;
  408. }
  409. }
  410. else if (pguidCmdGroup && IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
  411. {
  412. hr = S_OK;
  413. switch (nCmdID)
  414. {
  415. case SBCMDID_ERRORPAGE:
  416. {
  417. // We save urls to error pages so that they don't get placed
  418. // into the MRU
  419. if (pvarargIn && pvarargIn->vt == VT_BSTR)
  420. {
  421. // Save the location where the error occured
  422. Str_SetPtr(&m_pszHttpErrorUrl, pvarargIn->bstrVal);
  423. }
  424. break;
  425. }
  426. case SBCMDID_AUTOSEARCHING:
  427. {
  428. // The address did not resolve so the string is about to be sent
  429. // to the search engine or autoscanned. There is a good chance
  430. // the pending url had "http:\\" prefixed which is a bogus url.
  431. // So let's put what the user typed into the mru instead.
  432. //
  433. Str_SetPtr(&m_pszPendingURL, m_pszUserEnteredURL);
  434. break;
  435. }
  436. case SBCMDID_GETUSERADDRESSBARTEXT:
  437. UINT cb = (m_pszUserEnteredURL ? (lstrlen(m_pszUserEnteredURL) + 1) : 0);
  438. BSTR bstr = NULL;
  439. VariantInit(pvarargOut);
  440. if (cb)
  441. bstr = SysAllocStringLen(NULL, cb);
  442. if (bstr)
  443. {
  444. SHTCharToUnicode(m_pszUserEnteredURL, bstr, cb);
  445. pvarargOut->vt = VT_BSTR|VT_BYREF;
  446. pvarargOut->byref = bstr;
  447. }
  448. else
  449. {
  450. // VariantInit() might do this for us.
  451. pvarargOut->vt = VT_EMPTY;
  452. pvarargOut->byref = NULL;
  453. return E_FAIL; // Edit_GetText gave us nothing
  454. }
  455. break;
  456. }
  457. }
  458. else if (pguidCmdGroup && IsEqualGUID(CGID_AddressEditBox, *pguidCmdGroup))
  459. {
  460. switch (nCmdID)
  461. {
  462. case AECMDID_SAVE:
  463. hr = Save(0);
  464. break;
  465. default:
  466. hr = E_NOTIMPL;
  467. break;
  468. }
  469. }
  470. return(hr);
  471. }
  472. //================================
  473. // ** IWinEventHandler Interface ***
  474. /****************************************************\
  475. FUNCTION: OnWinEvent
  476. DESCRIPTION:
  477. This function will give receive events from
  478. the parent ShellToolbar.
  479. \****************************************************/
  480. HRESULT CAddressEditBox::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
  481. {
  482. LRESULT lres = 0;
  483. switch (uMsg) {
  484. case WM_WININICHANGE:
  485. {
  486. HWND hwndLocal = (m_hwnd ? m_hwnd : m_hwndEdit);
  487. if (hwndLocal)
  488. SendMessage(hwndLocal, uMsg, wParam, lParam);
  489. // MRU Needs it because it May Need to purge the MRU even if it isn't the current list.
  490. if ((m_palCurrent != m_palMRU) && m_palMRU)
  491. m_palMRU->OnWinEvent(m_hwnd, uMsg, wParam, lParam, plres);
  492. _SetAutocompleteOptions();
  493. }
  494. break;
  495. case WM_COMMAND:
  496. lres = _OnCommand(wParam, lParam);
  497. break;
  498. case WM_NOTIFY:
  499. lres = _OnNotify((LPNMHDR)lParam);
  500. break;
  501. }
  502. if (plres)
  503. *plres = lres;
  504. // All Events get all events, and they need to determine
  505. // if they are active to act on most of the events.
  506. if (m_hwnd)
  507. {
  508. if (m_palCurrent)
  509. {
  510. m_palCurrent->OnWinEvent(m_hwnd, uMsg, wParam, lParam, plres);
  511. }
  512. // If we are dropping down the list, the above call could have
  513. // changed the selection, so grab it again...
  514. if ((uMsg == WM_COMMAND) && (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_DROPDOWN))
  515. {
  516. m_nOldSelection = ComboBox_GetCurSel(m_hwnd);
  517. // If nothing selected, and something matches the contents of the editbox, select that
  518. if (m_nOldSelection == -1)
  519. {
  520. TCHAR szBuffer[MAX_URL_STRING];
  521. GetWindowText(m_hwnd, szBuffer, SIZECHARS(szBuffer));
  522. m_nOldSelection = (int)SendMessage(m_hwnd, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)szBuffer);
  523. if (m_nOldSelection != CB_ERR)
  524. {
  525. ComboBox_SetCurSel(m_hwnd, m_nOldSelection);
  526. }
  527. }
  528. }
  529. }
  530. return S_OK;
  531. }
  532. /****************************************************\
  533. FUNCTION: IsWindowOwner
  534. DESCRIPTION:
  535. This function will return TRUE if the HWND
  536. passed in is a HWND owned by this band.
  537. \****************************************************/
  538. HRESULT CAddressEditBox::IsWindowOwner(HWND hwnd)
  539. {
  540. if (hwnd == m_hwnd)
  541. return S_OK;
  542. if (m_hwndEdit && (hwnd == m_hwndEdit))
  543. return S_OK;
  544. return S_FALSE;
  545. }
  546. void CAddressEditBox::_GetUrlAndCache(void)
  547. {
  548. TCHAR szTemp[MAX_URL_STRING];
  549. // This will fail when the browser first opens and the first navigation to the
  550. // default home page doesn't start downloading yet.
  551. if (SUCCEEDED(m_pshuUrl->GetUrl(szTemp, SIZECHARS(szTemp))))
  552. {
  553. SHRemoveURLTurd(szTemp);
  554. SHCleanupUrlForDisplay(szTemp);
  555. Str_SetPtr(&m_pszCurrentUrl, szTemp); // Used when refreshing
  556. }
  557. else
  558. {
  559. Str_SetPtr(&m_pszCurrentUrl, NULL);
  560. }
  561. }
  562. //================================
  563. // *** IDispatch Interface ***
  564. /****************************************************\
  565. FUNCTION: Invoke
  566. DESCRIPTION:
  567. This function will give receive events from
  568. the Browser Window if this band is connected
  569. to one. This will allow this band to remain up
  570. todate when the browser window changes URL by
  571. another means.
  572. \****************************************************/
  573. HRESULT CAddressEditBox::Invoke(DISPID dispidMember,REFIID riid,LCID lcid,WORD wFlags,
  574. DISPPARAMS * pdispparams, VARIANT * pvarResult,
  575. EXCEPINFO * pexcepinfo,UINT * puArgErr)
  576. {
  577. HRESULT hr = S_OK;
  578. ASSERT(pdispparams);
  579. if (!pdispparams)
  580. return E_INVALIDARG;
  581. switch(dispidMember)
  582. {
  583. case DISPID_NAVIGATECOMPLETE: // This is when we have bits back?
  584. ASSERT(0); // We didn't ask to synch these.
  585. break;
  586. // The event DISPID_NAVIGATECOMPLETE2 may be sent several times during
  587. // redirects.
  588. // The event DISPID_DOCUMENTCOMPLETE will only happen after navigation is
  589. // finished.
  590. case DISPID_DOCUMENTCOMPLETE:
  591. Str_SetPtr(&m_pszUserEnteredURL, NULL);
  592. break;
  593. case DISPID_NAVIGATECOMPLETE2:
  594. {
  595. DWORD dwCurrent;
  596. BOOL fFound = FALSE;
  597. ASSERT(m_elt != LT_NONE);
  598. IBrowserService* pbs = NULL;
  599. for (dwCurrent = 0; dwCurrent < pdispparams->cArgs; dwCurrent++)
  600. {
  601. if (pdispparams->rgvarg[dwCurrent].vt == VT_DISPATCH)
  602. {
  603. // See who's sending us this event
  604. hr = IUnknown_QueryService(pdispparams->rgvarg[dwCurrent].pdispVal, SID_SShellBrowser, IID_IBrowserService, (void**)&pbs);
  605. if (pbs)
  606. {
  607. // We don't really need this interface, just its address
  608. pbs->Release();
  609. }
  610. if (FAILED(hr) || pbs != m_pbs)
  611. {
  612. // Notification must have come from a frame, so ignore it because
  613. // it doesn't affect the URL in the address bar.
  614. return S_OK;
  615. }
  616. }
  617. else if (!fFound)
  618. {
  619. if ((pdispparams->rgvarg[dwCurrent].vt == VT_BSTR) ||
  620. ((pdispparams->rgvarg[dwCurrent].vt == (VT_VARIANT|VT_BYREF)) &&
  621. (pdispparams->rgvarg[dwCurrent].pvarVal->vt == VT_BSTR)))
  622. {
  623. fFound = TRUE;
  624. }
  625. }
  626. }
  627. ASSERT(fFound);
  628. hr = _CreateCShellUrl();
  629. if (FAILED(hr))
  630. return hr;
  631. // Yes, so let's set our current working directory to the current window.
  632. ASSERT(m_pbs);
  633. LPITEMIDLIST pidl;
  634. if (SUCCEEDED(hr = m_pbs->GetPidl(&pidl)))
  635. {
  636. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  637. TraceMsg(TF_BAND|TF_GENERAL, "CAddressEditBox::Invoke(), Current Pidl in TravelLog. PIDL=%s;", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  638. ASSERT(pidl);
  639. // m_pshuUrl will free pshuCurrWorkDir, so we can't.
  640. hr = m_pshuUrl->SetPidl(pidl);
  641. ILFree(pidl);
  642. _GetUrlAndCache(); // We call this function so stack space is only used temporarily. It will set m_pszCurrentUrl.
  643. if (SUCCEEDED(hr))
  644. {
  645. LPTSTR pszTempURL = NULL;
  646. // WARNING: This code looks really strange, but it is necessary. Normally,
  647. // I would like to pass m_pszCurrentUrl as an arg to _NavigationComplete. The problem
  648. // is that the _NavigationComplete calls m_palCurrent->NavigationComplete() which will replace
  649. // the value in m_pszCurrentUrl. So I need to pass a value that will still be valid when
  650. // m_pszCurrentUrl gets reoplaced.
  651. // (That function causes the string to change values indirectly because it ends up sending
  652. // a CBEM_SETITEM message to the combobox which will update m_pszCurrentUrl.)
  653. //
  654. // We put this string on the heap because it can be very large (MAX_URL_STRING) and
  655. // this code that calls us and the code we call, use an incredible amount of stack space.
  656. // This code needs to highly optimize on how much stack space it uses or it will cause
  657. // out of memory faults when trying to grow the stack.
  658. Str_SetPtr(&pszTempURL, m_pszCurrentUrl);
  659. if (pszTempURL)
  660. {
  661. hr = _NavigationComplete(pszTempURL, TRUE, TRUE);
  662. }
  663. Str_SetPtr(&pszTempURL, NULL);
  664. }
  665. }
  666. else
  667. {
  668. Str_SetPtr(&m_pszCurrentUrl, NULL); // Init incase it's null
  669. }
  670. }
  671. break;
  672. default:
  673. hr = E_INVALIDARG;
  674. }
  675. return hr;
  676. }
  677. /****************************************************\
  678. FUNCTION: _UseNewList
  679. DESCRIPTION:
  680. This function will switch the list we use to
  681. populate the contents of the combobox.
  682. \****************************************************/
  683. HRESULT CAddressEditBox::_UseNewList(ENUMLISTTYPE eltNew)
  684. {
  685. HRESULT hr = S_OK;
  686. ASSERT(m_hwnd); // It's invalid for use to use a AddressList if we are only and EditBox.
  687. if (m_elt == eltNew)
  688. return S_OK; // We are already using this list.
  689. if (m_palCurrent)
  690. {
  691. m_palCurrent->Connect(FALSE, m_hwnd, m_pbs, m_pbp, m_pac);
  692. m_palCurrent->Release();
  693. }
  694. switch(eltNew)
  695. {
  696. case LT_SHELLNAMESPACE:
  697. ASSERT(m_palSNS);
  698. m_palCurrent = m_palSNS;
  699. break;
  700. case LT_TYPEIN_MRU:
  701. ASSERT(m_palMRU);
  702. m_palCurrent = m_palMRU;
  703. break;
  704. default:
  705. ASSERT(0); // Someone messed up.
  706. m_palCurrent = NULL;
  707. break;
  708. }
  709. if (m_palCurrent)
  710. {
  711. m_palCurrent->AddRef();
  712. m_palCurrent->Connect(TRUE, m_hwnd, m_pbs, m_pbp, m_pac);
  713. }
  714. m_elt = eltNew;
  715. return hr;
  716. }
  717. //================================
  718. // *** IAddressEditBox Interface ***
  719. /****************************************************\
  720. FUNCTION: Save
  721. DESCRIPTION:
  722. \****************************************************/
  723. HRESULT CAddressEditBox::Save(DWORD dwReserved)
  724. {
  725. HRESULT hr = S_OK;
  726. ASSERT(0 == dwReserved); // Reserved for later.
  727. if (m_palMRU)
  728. hr = m_palMRU->Save();
  729. return hr;
  730. }
  731. /****************************************************\
  732. FUNCTION: Init
  733. PARAMETERS:
  734. hwnd - Points to ComboBoxEx otherwise NULL.
  735. hwndEditBox - EditBox.
  736. dwFlags - AEB_INIT_XXXX flags (Defined in iedev\inc\shlobj.w)
  737. punkParent - Pointer to parent object that should receive events.
  738. DESCRIPTION:
  739. This function will Hook this CAddressEditBox
  740. object to the ComboBoxEx or EditBox control. If
  741. this object is being hooked up to a ComboBoxEx control,
  742. then hwnd is of the ComboBoxEx control and hwndEditBox
  743. is of that ComboBox's edit control. If this is
  744. being hooked up to only an EditBox, then hwnd is NULL
  745. and hwndEditBox points to the edit box. If punkParent
  746. is NULL, we will not be connected to a browser window
  747. at all.
  748. \****************************************************/
  749. HRESULT CAddressEditBox::Init(HWND hwnd, OPTIONAL
  750. HWND hwndEditBox,
  751. DWORD dwFlags,
  752. IUnknown * punkParent) OPTIONAL
  753. {
  754. HRESULT hr = S_OK;
  755. ASSERT(!m_hwnd);
  756. m_hwnd = hwnd;
  757. m_hwndEdit = hwndEditBox;
  758. m_dwFlags = dwFlags;
  759. IUnknown_Set(&m_punkParent, punkParent);
  760. // Get and save our top-level window
  761. m_hwndBrowser = hwnd;
  762. HWND hwndParent;
  763. while (hwndParent = GetParent(m_hwndBrowser))
  764. {
  765. m_hwndBrowser = hwndParent;
  766. }
  767. ASSERT(!(AEB_INIT_SUBCLASS &dwFlags)); // We don't support this yet.
  768. if (hwnd) // Is this a ComboBox?
  769. {
  770. // Yes,
  771. ASSERT(!m_palSNS && !m_palMRU /*&& !m_palACP*/);
  772. m_palSNS = CSNSList_Create();
  773. m_palMRU = CMRUList_Create();
  774. if (!m_palSNS || !m_palMRU /*|| !m_palACP*/)
  775. {
  776. hr = E_FAIL;
  777. }
  778. if (SUCCEEDED(hr))
  779. {
  780. HWND hwndCombo;
  781. hwndCombo = (HWND)SendMessage(m_hwnd, CBEM_GETCOMBOCONTROL, 0, 0);
  782. if (!hwndCombo)
  783. hr = E_FAIL; // This will happen if the user passed in a ComboBox instead of a ComboBoxEx for hwnd.
  784. if (hwndCombo && SetProp(hwndCombo, SZ_ADDRESSCOMBO_PROP, this))
  785. {
  786. g_hWinStationBefore = GetProcessWindowStation();
  787. // Subclass combobox for various tweaks.
  788. ASSERT(!m_lpfnComboWndProc);
  789. m_lpfnComboWndProc = (WNDPROC) SetWindowLongPtr(hwndCombo, GWLP_WNDPROC, (LONG_PTR) _ComboSubclassWndProc);
  790. TraceMsg(TF_BAND|TF_GENERAL, "CAddressEditBox::Init() wndproc=%x", m_lpfnComboWndProc);
  791. // Subclass the comboboxex too
  792. if (SetProp(hwnd, SZ_ADDRESSCOMBOEX_PROP, this))
  793. {
  794. ASSERT(!m_lpfnComboExWndProc);
  795. m_lpfnComboExWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)_ComboExSubclassWndProc);
  796. }
  797. }
  798. }
  799. }
  800. if (SUCCEEDED(hr))
  801. {
  802. //
  803. // Set g_himl*
  804. //
  805. ASSERT(!m_pbp);
  806. hr = QueryService_SID_IBandProxy(punkParent, IID_IBandProxy, &m_pbp, NULL);
  807. // We need to set the list to MRU for the first time.
  808. // We need to do this to initialize the list because
  809. // it will be used even when other lists are selected.
  810. if (m_hwnd && LT_NONE == m_elt)
  811. _UseNewList(LT_TYPEIN_MRU);
  812. }
  813. if (hwndEditBox) {
  814. SendMessage(hwndEditBox, EM_SETLIMITTEXT, INTERNET_MAX_PATH_LENGTH - 1, 0);
  815. }
  816. return hr;
  817. }
  818. /****************************************************\
  819. FUNCTION: SetOwner
  820. PARAMETERS:
  821. punkOwner - Pointer to the parent object.
  822. DESCRIPTION:
  823. This function will be called to have this
  824. object try to obtain enough information about it's
  825. parent Toolbar to create the Band window and maybe
  826. connect to a Browser Window.
  827. \****************************************************/
  828. HRESULT CAddressEditBox::SetOwner(IUnknown* punkOwner)
  829. {
  830. HRESULT hr = S_OK;
  831. if (m_pbs)
  832. _ConnectToBrwsrWnd(NULL); // On-connect from Browser Window.
  833. if (m_hwnd && !punkOwner)
  834. {
  835. if (m_palSNS)
  836. m_palSNS->Save();
  837. if (m_palMRU)
  838. m_palMRU->Save();
  839. }
  840. IUnknown_Set(&m_punkParent, punkOwner); // Needed to break ref count cycle.
  841. _ConnectToBrwsrWnd(punkOwner); // On-connect from Browser Window.
  842. return hr;
  843. }
  844. /****************************************************\
  845. FUNCTION: SetCurrentDir
  846. DESCRIPTION:
  847. Set the Current Working directory so parsing
  848. will work correctly.
  849. \****************************************************/
  850. HRESULT CAddressEditBox::SetCurrentDir(LPCOLESTR pwzDir)
  851. {
  852. HRESULT hr;
  853. SHSTR strWorkingDir;
  854. hr = strWorkingDir.SetStr(pwzDir);
  855. if (SUCCEEDED(hr))
  856. {
  857. LPITEMIDLIST pidl;
  858. hr = IECreateFromPath(strWorkingDir.GetStr(), &pidl);
  859. if (SUCCEEDED(hr))
  860. {
  861. hr = _CreateCShellUrl();
  862. ASSERT(SUCCEEDED(hr));
  863. if (SUCCEEDED(hr))
  864. hr = m_pshuUrl->SetCurrentWorkingDir(pidl);
  865. ILFree(pidl);
  866. }
  867. }
  868. return hr;
  869. }
  870. /****************************************************\
  871. FUNCTION: ParseNow
  872. PARAMETERS:
  873. dwFlags - Parse Flags
  874. DESCRIPTION:
  875. Parse the text that is currently in the EditBox.
  876. \****************************************************/
  877. HRESULT CAddressEditBox::ParseNow(DWORD dwFlags)
  878. {
  879. HRESULT hr;
  880. TCHAR szBuffer[MAX_URL_STRING];
  881. ASSERT(m_hwnd);
  882. GetWindowText(m_hwnd, szBuffer, SIZECHARS(szBuffer));
  883. hr = _CreateCShellUrl();
  884. ASSERT(SUCCEEDED(hr));
  885. if (SUCCEEDED(hr))
  886. {
  887. if (m_fConnectedToBrowser && !SHRegGetBoolUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Band\\Address"), TEXT("Use Path"), FALSE, FALSE))
  888. {
  889. dwFlags |= SHURL_FLAGS_NOPATHSEARCH;
  890. }
  891. if (SHRegGetBoolUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Band\\Address"), TEXT("AutoCorrect"), FALSE, /*default*/TRUE))
  892. {
  893. dwFlags |= SHURL_FLAGS_AUTOCORRECT;
  894. }
  895. hr = m_pshuUrl->ParseFromOutsideSource(szBuffer, dwFlags);
  896. }
  897. return hr;
  898. }
  899. /****************************************************\
  900. FUNCTION: Execute
  901. PARAMETERS:
  902. dwExecFlags - Execute Flags
  903. DESCRIPTION:
  904. This function will execute the last parsed string.
  905. In most cases, the caller should call ::ParseNow()
  906. first.
  907. \****************************************************/
  908. HRESULT CAddressEditBox::Execute(DWORD dwExecFlags)
  909. {
  910. HRESULT hr = E_FAIL;
  911. ASSERT(m_pshuUrl);
  912. TCHAR szShortcutFilePath[MAX_PATH];
  913. LPITEMIDLIST pidl;
  914. hr = m_pshuUrl->GetPidlNoGenerate(&pidl);
  915. if (SUCCEEDED(hr))
  916. {
  917. hr = IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szShortcutFilePath, SIZECHARS(szShortcutFilePath), NULL);
  918. ILFree(pidl);
  919. }
  920. // if this is a .url and we can navigate to it
  921. // then we need to do that now, otherwise
  922. // we'll end up with a shell exec happening
  923. // which will open the .url in whatever
  924. // browse window the system happens to like
  925. if (SUCCEEDED(hr))
  926. {
  927. ASSERT(m_punkParent != NULL);
  928. // try navigating in the current browser window
  929. // NavFrameWithFile will exit without doing
  930. // anything if we're not dealing with a .url
  931. hr = NavFrameWithFile(szShortcutFilePath, m_punkParent);
  932. }
  933. // it's not a .url or we can't nav to it for some reason
  934. // let the general handlers have a shot now
  935. if (FAILED(hr))
  936. {
  937. hr = m_pshuUrl->Execute(m_pbp, &m_fDidShellExec,dwExecFlags);
  938. }
  939. return hr;
  940. }
  941. //================================
  942. // *** IAddressBand Interface ***
  943. /****************************************************\
  944. FUNCTION: FileSysChange
  945. DESCRIPTION:
  946. This function will handle file system change
  947. notifications.
  948. \****************************************************/
  949. HRESULT CAddressEditBox::FileSysChange(DWORD dwEvent, LPCITEMIDLIST *ppidl)
  950. {
  951. // m_hwnd == NULL means we don't need to do anything
  952. // however we will probably never get that event
  953. // if that is the case.
  954. if (m_palSNS)
  955. m_palSNS->FileSysChangeAL(dwEvent, ppidl);
  956. return S_OK;
  957. }
  958. /****************************************************\
  959. FUNCTION: Refresh
  960. PARAMETERS:
  961. pvarType - NULL for a refress of everything.
  962. OLECMD_REFRESH_TOPMOST will only update the top most.
  963. DESCRIPTION:
  964. This function will force a refress of part
  965. or all of the AddressBand.
  966. \****************************************************/
  967. HRESULT CAddressEditBox::Refresh(VARIANT * pvarType)
  968. {
  969. //
  970. // Refreshing does not automatically refresh the contents of the
  971. // edit window because a DISPID_DOCUMENTCOMPLETE or DISPID_NAVIGATECOMPLETE2
  972. // is not sent. So we restore the contents ourselves.
  973. //
  974. if (m_hwndEdit && m_pszCurrentUrl && !IsErrorUrl(m_pszCurrentUrl))
  975. {
  976. TCHAR szTemp[MAX_URL_STRING];
  977. StringCchCopy(szTemp, ARRAYSIZE(szTemp), m_pszCurrentUrl);
  978. SendMessage(m_hwndEdit, WM_SETTEXT, (WPARAM)0, (LPARAM)szTemp);
  979. }
  980. DWORD dwType = OLECMD_REFRESH_ENTIRELIST; // Default
  981. if (pvarType)
  982. {
  983. if (VT_I4 != pvarType->vt)
  984. return E_INVALIDARG;
  985. dwType = pvarType->lVal;
  986. }
  987. if (m_hwnd && m_palCurrent && m_pbs)
  988. {
  989. if (!m_pszCurrentUrl)
  990. {
  991. if (!m_pshuUrl)
  992. {
  993. _CreateCShellUrl();
  994. }
  995. LPITEMIDLIST pidl;
  996. if (SUCCEEDED(m_pbs->GetPidl(&pidl)))
  997. {
  998. if (SUCCEEDED(m_pshuUrl->SetPidl(pidl)) && m_pshuUrl)
  999. {
  1000. TCHAR szDisplayName[MAX_URL_STRING];
  1001. if (SUCCEEDED(m_pshuUrl->GetUrl(szDisplayName, ARRAYSIZE(szDisplayName))))
  1002. {
  1003. Str_SetPtr(&m_pszCurrentUrl, szDisplayName);
  1004. }
  1005. }
  1006. ILFree(pidl);
  1007. }
  1008. }
  1009. if (m_pszCurrentUrl)
  1010. {
  1011. _UseNewList(PathIsURL(m_pszCurrentUrl) ? LT_TYPEIN_MRU : LT_SHELLNAMESPACE);
  1012. if (m_palCurrent)
  1013. {
  1014. m_palCurrent->Connect(TRUE, m_hwnd, m_pbs, m_pbp, m_pac);
  1015. m_palCurrent->Refresh(dwType);
  1016. }
  1017. }
  1018. }
  1019. return S_OK;
  1020. }
  1021. //================================
  1022. // *** Internal/Private Methods ***
  1023. //=================================================================
  1024. // General Band Functions
  1025. //=================================================================
  1026. /****************************************************\
  1027. Address Band Constructor
  1028. \****************************************************/
  1029. CAddressEditBox::CAddressEditBox()
  1030. {
  1031. DllAddRef();
  1032. TraceMsg(TF_SHDLIFE, "ctor CAddressEditBox %x", this);
  1033. m_cRef = 1;
  1034. // This needs to be allocated in Zero Inited Memory.
  1035. // ASSERT that all Member Variables are inited to Zero.
  1036. ASSERT(!m_punkParent);
  1037. ASSERT(!m_hwnd);
  1038. ASSERT(!m_hwndEdit);
  1039. ASSERT(!m_lpfnComboWndProc);
  1040. ASSERT(!m_pbp);
  1041. ASSERT(!m_pbs);
  1042. ASSERT(!m_dwcpCookie);
  1043. ASSERT(!m_pszCurrentUrl);
  1044. ASSERT(!m_pszPendingURL);
  1045. ASSERT(!m_pac);
  1046. ASSERT(!m_pssACLISF);
  1047. ASSERT(!m_palCurrent);
  1048. ASSERT(!m_palSNS);
  1049. ASSERT(!m_palMRU);
  1050. ASSERT(!m_pmru);
  1051. ASSERT(!m_pshuUrl);
  1052. ASSERT(!m_fDidShellExec);
  1053. ASSERT(!m_pszUserEnteredURL);
  1054. ASSERT(!m_fConnectedToBrowser);
  1055. ASSERT(!m_pAsyncNav);
  1056. ASSERT(!m_fAsyncNavInProgress);
  1057. ASSERT(AEB_INIT_DEFAULT == m_dwFlags);
  1058. m_nOldSelection = -1;
  1059. m_elt = LT_NONE;
  1060. m_cbex.mask = 0;
  1061. m_cbex.pszText = 0;
  1062. m_cbex.cchTextMax = 0;
  1063. if (!g_nAEB_AsyncNavigation)
  1064. g_nAEB_AsyncNavigation = RegisterWindowMessage(TEXT("CAEBAsyncNavigation"));
  1065. }
  1066. /****************************************************\
  1067. Address Band destructor
  1068. \****************************************************/
  1069. CAddressEditBox::~CAddressEditBox()
  1070. {
  1071. _CancelNavigation();
  1072. ATOMICRELEASE(m_punkParent);
  1073. ATOMICRELEASE(m_pac);
  1074. ATOMICRELEASE(m_pssACLISF);
  1075. ATOMICRELEASE(m_palSNS);
  1076. ATOMICRELEASE(m_palMRU);
  1077. ATOMICRELEASE(m_palCurrent);
  1078. ATOMICRELEASE(m_pbp);
  1079. ATOMICRELEASE(m_pbs);
  1080. ATOMICRELEASE(m_pmru);
  1081. if (m_pshuUrl)
  1082. {
  1083. delete m_pshuUrl;
  1084. }
  1085. Str_SetPtr(&m_pszCurrentUrl, NULL);
  1086. Str_SetPtr(&m_pszPendingURL, NULL);
  1087. Str_SetPtr(&m_pszUserEnteredURL, NULL);
  1088. Str_SetPtr(&m_pszHttpErrorUrl, NULL);
  1089. _RemoveHook();
  1090. if (m_fAssociated)
  1091. {
  1092. m_al.Delete(GetCurrentThreadId());
  1093. }
  1094. Str_SetPtr(&m_cbex.pszText, NULL);
  1095. TraceMsg(TF_SHDLIFE, "dtor CAddressEditBox %x", this);
  1096. DllRelease();
  1097. }
  1098. /****************************************************\
  1099. FUNCTION: CAddressEditBox_CreateInstance
  1100. DESCRIPTION:
  1101. This function will create an instance of the
  1102. AddressBand COM object.
  1103. \****************************************************/
  1104. HRESULT CAddressEditBox_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  1105. {
  1106. // aggregation checking is handled in class factory
  1107. *ppunk = NULL;
  1108. CAddressEditBox * p = new CAddressEditBox();
  1109. if (p)
  1110. {
  1111. *ppunk = SAFECAST(p, IAddressBand *);
  1112. return NOERROR;
  1113. }
  1114. return E_OUTOFMEMORY;
  1115. }
  1116. /****************************************************\
  1117. FUNCTION: _OnNotify
  1118. DESCRIPTION:
  1119. This function will handle WM_NOTIFY messages.
  1120. \****************************************************/
  1121. LRESULT CAddressEditBox::_OnNotify(LPNMHDR pnm)
  1122. {
  1123. // HACKHACK: combobox (comctl32\comboex.c) will pass a LPNMHDR, but it's really
  1124. // a PNMCOMBOBOXEX (which has a first element of LPNMHDR). This function
  1125. // can use this type cast iff it's guaranteed that this will only come from
  1126. // a function that behaves in this perverse way.
  1127. PNMCOMBOBOXEX pnmce = (PNMCOMBOBOXEX)pnm;
  1128. ASSERT(pnm);
  1129. switch (pnm->code)
  1130. {
  1131. case CBEN_BEGINEDIT:
  1132. _OnBeginEdit(pnm);
  1133. break;
  1134. case CBEN_ENDEDITA:
  1135. _OnEndEditA((LPNMCBEENDEDITA)pnm);
  1136. TraceMsg(TF_BAND|TF_GENERAL, "CAddressEditBox::_OnNotify(), pnm->code=CBEN_ENDEDITA");
  1137. break;
  1138. case CBEN_ENDEDITW:
  1139. _OnEndEditW((LPNMCBEENDEDITW)pnm);
  1140. TraceMsg(TF_BAND|TF_GENERAL, "CAddressEditBox::_OnNotify(), pnm->code=CBEN_ENDEDITW");
  1141. break;
  1142. default:
  1143. break;
  1144. }
  1145. return 0;
  1146. }
  1147. LRESULT CAddressEditBox::_OnBeginEdit(LPNMHDR pnm)
  1148. {
  1149. if (m_punkParent)
  1150. IUnknown_OnFocusChangeIS(m_punkParent, m_punkParent, TRUE);
  1151. return 0;
  1152. }
  1153. /****************************************************\
  1154. FUNCTION: _OnEndEditW
  1155. DESCRIPTION:
  1156. Thunk to _OnEndEditA.
  1157. \****************************************************/
  1158. LRESULT CAddressEditBox::_OnEndEditW(LPNMCBEENDEDITW pnmW)
  1159. {
  1160. NMCBEENDEDITA nmA;
  1161. nmA.hdr = pnmW->hdr;
  1162. nmA.fChanged = pnmW->fChanged;
  1163. nmA.iNewSelection = pnmW->iNewSelection;
  1164. nmA.iWhy = pnmW->iWhy;
  1165. // don't we lose unicode information on this transition?!
  1166. // We don't use pnmw->szText so don't bother converting it
  1167. // SHUnicodeToAnsi(pnmW->szText, nmA.szText, ARRAYSIZE(nmA.szText));
  1168. nmA.szText[0] = 0;
  1169. return _OnEndEditA(&nmA);
  1170. }
  1171. /****************************************************\
  1172. FUNCTION: _OnEndEditA
  1173. DESCRIPTION:
  1174. Handle the WM_NOTIFY/CBEN_ENDEDITA message.
  1175. \****************************************************/
  1176. LRESULT CAddressEditBox::_OnEndEditA(LPNMCBEENDEDITA pnmA)
  1177. {
  1178. BOOL fRestoreIcons = TRUE;
  1179. ASSERT(pnmA);
  1180. //
  1181. // Navigate only if the user pressed enter in the edit control.
  1182. //
  1183. ASSERT(m_hwnd);
  1184. switch (pnmA->iWhy)
  1185. {
  1186. case CBENF_RETURN:
  1187. {
  1188. if (g_dwProfileCAP & 0x00000002) {
  1189. StartCAP();
  1190. }
  1191. // Use szUrl and ignore pnmA->szText because it truncates to MAX_PATH (=256)
  1192. TCHAR szUrl[MAX_URL_STRING];
  1193. if (m_hwndEdit)
  1194. {
  1195. // Allow the edit text to be updated
  1196. _ClearDirtyFlag();
  1197. GetWindowText(m_hwndEdit, szUrl, SIZECHARS(szUrl));
  1198. Str_SetPtr(&m_pszUserEnteredURL, szUrl);
  1199. // If edit box is empty, don't show icon
  1200. if (*szUrl == L'\0')
  1201. {
  1202. fRestoreIcons = FALSE;
  1203. }
  1204. #ifndef NO_ETW_TRACING
  1205. // Event trace for windows enable by shlwapi.
  1206. if (g_dwStopWatchMode & SPMODE_EVENTTRACE) {
  1207. EventTraceHandler(EVENT_TRACE_TYPE_BROWSE_ADDRESS,
  1208. szUrl);
  1209. }
  1210. #endif
  1211. if (g_dwStopWatchMode & (SPMODE_BROWSER | SPMODE_JAVA))
  1212. {
  1213. DWORD dwTime = GetPerfTime();
  1214. if (g_dwStopWatchMode & SPMODE_BROWSER) // Used to get browser total download time
  1215. StopWatch_StartTimed(SWID_BROWSER_FRAME, TEXT("Browser Frame Same"), SPMODE_BROWSER | SPMODE_DEBUGOUT, dwTime);
  1216. if (g_dwStopWatchMode & SPMODE_JAVA) // Used to get java applet load time
  1217. StopWatch_StartTimed(SWID_JAVA_APP, TEXT("Java Applet Same"), SPMODE_JAVA | SPMODE_DEBUGOUT, dwTime);
  1218. }
  1219. // If the WindowText matches the last URL we navigated
  1220. // to, then we need to call Refresh() instead of _HandleUserAction().
  1221. // This is because IWebBrowser2::Navigate2() ignores any commands that
  1222. // point to the same URL that it's already navigated to.
  1223. if (m_pszCurrentUrl && m_hwnd && !m_fDidShellExec &&
  1224. m_fConnectedToBrowser && (-1 == pnmA->iNewSelection) &&
  1225. (0 == lstrcmp(m_pszCurrentUrl, szUrl)))
  1226. {
  1227. IUnknown *punk = NULL;
  1228. // Refresh Browser.
  1229. if (m_pbp)
  1230. {
  1231. m_pbp->GetBrowserWindow(&punk);
  1232. }
  1233. if (punk) {
  1234. IWebBrowser* pwb;
  1235. punk->QueryInterface(IID_IWebBrowser, (LPVOID*)&pwb);
  1236. if (pwb) {
  1237. VARIANT v = {0};
  1238. v.vt = VT_I4;
  1239. v.lVal = OLECMDIDF_REFRESH_RELOAD|OLECMDIDF_REFRESH_CLEARUSERINPUT;
  1240. Refresh(NULL);
  1241. pwb->Refresh2(&v);
  1242. pwb->Release();
  1243. }
  1244. punk->Release();
  1245. }
  1246. }
  1247. else
  1248. {
  1249. SendMessage(m_hwnd, CB_SHOWDROPDOWN, FALSE, 0);
  1250. _HandleUserAction(szUrl, pnmA->iNewSelection);
  1251. }
  1252. UEMFireEvent(&UEMIID_BROWSER, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_NAVIGATE, UIBL_NAVADDRESS);
  1253. }
  1254. }
  1255. break;
  1256. case CBENF_KILLFOCUS:
  1257. fRestoreIcons = FALSE;
  1258. break;
  1259. case CBENF_ESCAPE:
  1260. // Abort and clear the dirty flag
  1261. _ClearDirtyFlag();
  1262. if (m_hwndEdit && m_pszCurrentUrl && m_cbex.mask != 0)
  1263. {
  1264. SendMessage(m_hwnd, CBEM_SETITEM, (WPARAM)0, (LPARAM)(LPVOID)&m_cbex);
  1265. }
  1266. SendMessage(m_hwnd, CB_SHOWDROPDOWN, FALSE, 0);
  1267. if (pnmA->iNewSelection != -1) {
  1268. SendMessage(m_hwnd, CB_SETCURSEL, pnmA->iNewSelection, 0);
  1269. }
  1270. fRestoreIcons = FALSE;
  1271. break;
  1272. }
  1273. if (fRestoreIcons)
  1274. {
  1275. SendMessage(m_hwnd, CBEM_SETEXTENDEDSTYLE, CBES_EX_NOEDITIMAGE, 0);
  1276. }
  1277. return 0;
  1278. }
  1279. /****************************************************\
  1280. FUNCTION: _ConnectToBrwsrWnd
  1281. DESCRIPTION:
  1282. The IUnknown parameter needs to point to an
  1283. object that supports the IBrowserService and
  1284. IWebBrowserApp interfaces.
  1285. \****************************************************/
  1286. HRESULT CAddressEditBox::_ConnectToBrwsrWnd(IUnknown* punk)
  1287. {
  1288. HRESULT hr = S_OK;
  1289. if (m_pbs) {
  1290. _ConnectToBrwsrConnectionPoint(FALSE, m_punkParent);
  1291. ATOMICRELEASE(m_pbs);
  1292. }
  1293. if (punk)
  1294. {
  1295. IUnknown * punkHack;
  1296. // HACK: We behave differently if we are hosted outside of a browser
  1297. // than we do if we are in a browser. This call does nothing
  1298. // but identify our host.
  1299. if (SUCCEEDED(IUnknown_QueryService(punk, SID_SShellDesktop, IID_IUnknown, (void**)&punkHack)))
  1300. punkHack->Release();
  1301. else
  1302. {
  1303. // No, we are not hosted on the desktop, so we can synch to the events of the browser.
  1304. hr = IUnknown_QueryService(punk, SID_STopLevelBrowser, IID_IBrowserService, (void**)&m_pbs);
  1305. if (SUCCEEDED(hr))
  1306. {
  1307. // We only want notifications if we are the AddressBar.
  1308. _ConnectToBrwsrConnectionPoint(TRUE, punk);
  1309. }
  1310. }
  1311. }
  1312. // TODO: At some point we will need to implement IPropertyBag so
  1313. // the parent can specify if they want us to behave as though
  1314. // we are contected or not. For now, we will use the fact
  1315. // that we are either have a IBrowserService pointer or not.
  1316. m_fConnectedToBrowser = BOOLIFY(m_pbs);
  1317. if (!m_pac)
  1318. {
  1319. // We need to wait to create the AutoComplete Lists until m_fConnectedToBrowser is set.
  1320. if (m_hwndEdit)
  1321. hr = SHUseDefaultAutoComplete(m_hwndEdit, NULL, &m_pac, &m_pssACLISF, m_fConnectedToBrowser);
  1322. if (SUCCEEDED(hr))
  1323. {
  1324. _SetAutocompleteOptions();
  1325. }
  1326. }
  1327. //
  1328. // Subclass edit control of the combobox. We do this here rather than when this
  1329. // class is initialized so that we are first in the chain to receive messages.
  1330. //
  1331. if (!m_lpfnEditWndProc && m_hwndEdit && SetProp(m_hwndEdit, SZ_ADDRESSCOMBO_PROP, this))
  1332. {
  1333. m_lpfnEditWndProc = (WNDPROC)SetWindowLongPtr(m_hwndEdit, GWLP_WNDPROC, (LONG_PTR) _EditSubclassWndProc);
  1334. }
  1335. // This function will be called if: 1) we are becoming connected to a
  1336. // browser, 2) switch from one browser to another, or 3) are
  1337. // becoming unconnected from a browser. In any case, we need to
  1338. // update the ISF AutoComplete List so it can retrieve
  1339. // the current location from the appropriate browser.
  1340. if (m_pssACLISF)
  1341. m_pssACLISF->SetOwner(m_pbs);
  1342. return hr;
  1343. }
  1344. /****************************************************\
  1345. FUNCTION: _ConnectToBrwsrConnectionPoint
  1346. DESCRIPTION:
  1347. Connect to Browser Window's ConnectionPoint
  1348. that will provide events to let us keep up to date.
  1349. \****************************************************/
  1350. HRESULT CAddressEditBox::_ConnectToBrwsrConnectionPoint(BOOL fConnect, IUnknown * punk)
  1351. {
  1352. HRESULT hr = S_OK;
  1353. IConnectionPointContainer *pcpContainer;
  1354. if (punk)
  1355. {
  1356. hr = IUnknown_QueryService(punk, SID_SWebBrowserApp, IID_IConnectionPointContainer, (void **)&pcpContainer);
  1357. // Let's now have the Browser Window give us notification when something happens.
  1358. if (SUCCEEDED(hr))
  1359. {
  1360. hr = ConnectToConnectionPoint(SAFECAST(this, IDispatch*), DIID_DWebBrowserEvents2, fConnect,
  1361. pcpContainer, &m_dwcpCookie, NULL);
  1362. pcpContainer->Release();
  1363. }
  1364. }
  1365. return hr;
  1366. }
  1367. /****************************************************\
  1368. FUNCTION: _OnCommand
  1369. DESCRIPTION:
  1370. Handle WM_COMMAND messages.
  1371. \****************************************************/
  1372. LRESULT CAddressEditBox::_OnCommand(WPARAM wParam, LPARAM lParam)
  1373. {
  1374. UINT uCmd = GET_WM_COMMAND_CMD(wParam, lParam);
  1375. switch (uCmd)
  1376. {
  1377. case CBN_EDITCHANGE:
  1378. {
  1379. HWND hwndFocus = GetFocus();
  1380. if ((NULL != hwndFocus) && IsChild(m_hwnd, hwndFocus))
  1381. {
  1382. DWORD dwStyle = _IsDirty() ? CBES_EX_NOEDITIMAGE : 0;
  1383. SendMessage(m_hwnd, CBEM_SETEXTENDEDSTYLE, CBES_EX_NOEDITIMAGE, dwStyle);
  1384. }
  1385. break;
  1386. }
  1387. case CBN_CLOSEUP:
  1388. {
  1389. //
  1390. // Navigate to the selected string when the dropdown is not down.
  1391. //
  1392. int nSel = ComboBox_GetCurSel(m_hwnd);
  1393. if ((m_nOldSelection != SEL_ESCAPE_PRESSED) &&
  1394. (m_nOldSelection != nSel) && (nSel > -1))
  1395. {
  1396. _HandleUserAction(NULL, nSel);
  1397. // RedrawWindow eliminates annoying half-paint that
  1398. // occurs while navigating from one pidl to a smaller pidl.
  1399. RedrawWindow(m_hwnd, NULL, NULL, RDW_INTERNALPAINT | RDW_UPDATENOW);
  1400. }
  1401. }
  1402. if (m_pac)
  1403. m_pac->Enable(TRUE);
  1404. break;
  1405. case CBN_DROPDOWN:
  1406. if (m_pac)
  1407. m_pac->Enable(FALSE);
  1408. break;
  1409. }
  1410. return 0;
  1411. }
  1412. /*******************************************************************
  1413. FUNCTION: _CreateCShellUrl
  1414. DESCRIPTION:
  1415. Create the m_pshuUrl CShellUrl if needed.
  1416. ********************************************************************/
  1417. HRESULT CAddressEditBox::_CreateCShellUrl(void)
  1418. {
  1419. HRESULT hr = S_OK;
  1420. // Do we need to create our Shell Url?
  1421. if (!m_pshuUrl)
  1422. {
  1423. // Yes
  1424. m_pshuUrl = new CShellUrl();
  1425. if (!m_pshuUrl)
  1426. {
  1427. return E_FAIL;
  1428. }
  1429. else
  1430. {
  1431. m_pshuUrl->SetMessageBoxParent(m_hwndEdit);
  1432. // We need to set the "Shell Path" which will allow
  1433. // the user to enter Display Names of items in Shell
  1434. // Folders that are frequently used. We add "Desktop"
  1435. // and "Desktop/My Computer" to the Shell Path because
  1436. // that is what users use most often.
  1437. SetDefaultShellPath(m_pshuUrl);
  1438. }
  1439. }
  1440. return hr;
  1441. }
  1442. /*******************************************************************
  1443. FUNCTION: _HandleUserAction
  1444. PARAMETERS:
  1445. pszUrl - string of URL to navigate to.
  1446. iNewSelection - index of current selection in address bar combo box
  1447. DESCRIPTION:
  1448. Called when the user types in or selects a URL to navigate
  1449. to through the address bar.
  1450. ********************************************************************/
  1451. HRESULT CAddressEditBox::_HandleUserAction(LPCTSTR pszUrl, int iNewSelection)
  1452. {
  1453. HRESULT hr = S_OK;
  1454. TCHAR szDisplayName[MAX_URL_STRING];
  1455. HCURSOR hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1456. static DWORD dwParseFlags = 0xFFFFFFFF;
  1457. Str_SetPtr(&m_pszPendingURL, NULL); // Clear if one exists.
  1458. Str_SetPtr(&m_pszHttpErrorUrl, NULL);
  1459. hr = _CreateCShellUrl();
  1460. if (FAILED(hr))
  1461. return hr;
  1462. // Are we connected to a Browser Window?
  1463. if (m_pbs)
  1464. {
  1465. // Yes, so let's set our current working directory to the current window.
  1466. LPITEMIDLIST pidl;
  1467. m_pbs->GetPidl(&pidl);
  1468. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  1469. TraceMsg(TF_BAND|TF_GENERAL, "CAddressEditBox::_HandleUserAction(), Current Pidl in TravelLog. PIDL=%s;", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  1470. if (pidl)
  1471. {
  1472. // m_pshuUrl will free pshuCurrWorkDir, so we can't.
  1473. hr = m_pshuUrl->SetCurrentWorkingDir(pidl);
  1474. ILFree(pidl);
  1475. }
  1476. }
  1477. if (SUCCEEDED(hr))
  1478. {
  1479. // Cancel previous pending nav if any
  1480. _CancelNavigation();
  1481. // Did the user select the item from the drop down list?
  1482. if (-1 != iNewSelection)
  1483. {
  1484. // Yes, so point our CShellUrl at the item. (Pidl or URL)
  1485. if (m_palCurrent)
  1486. m_palCurrent->SetToListIndex(iNewSelection, (LPVOID) m_pshuUrl);
  1487. // if the index indicates this was a selection from the combo box,
  1488. // remember which selection it was
  1489. SendMessage(m_hwnd, CB_SETCURSEL, (WPARAM)iNewSelection, 0L);
  1490. *szDisplayName = L'\0';
  1491. GetWindowText(m_hwnd, szDisplayName, ARRAYSIZE(szDisplayName));
  1492. Str_SetPtr(&m_pszUserEnteredURL, szDisplayName);
  1493. pszUrl = NULL;
  1494. }
  1495. else
  1496. {
  1497. // No, the user hit return with some string.
  1498. ASSERT(pszUrl); // must have valid URL
  1499. if (0xFFFFFFFF == dwParseFlags)
  1500. {
  1501. dwParseFlags = SHURL_FLAGS_NONE;
  1502. if (m_fConnectedToBrowser && !SHRegGetBoolUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Band\\Address"), TEXT("Use Path"), FALSE, FALSE))
  1503. dwParseFlags = SHURL_FLAGS_NOPATHSEARCH;
  1504. if (SHRegGetBoolUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Band\\Address"), TEXT("AutoCorrect"), FALSE, /*default*/TRUE))
  1505. dwParseFlags |= SHURL_FLAGS_AUTOCORRECT;
  1506. }
  1507. }
  1508. hr = E_FAIL;
  1509. if (m_hwnd && m_pshuUrl)
  1510. {
  1511. if (!(m_dwFlags & AEB_INIT_NOASYNC)) // Is async navigate enabled?
  1512. {
  1513. // Create and initialize the AsyncNav object used to communicate with the thread
  1514. m_pAsyncNav = new AsyncNav();
  1515. if (m_pAsyncNav)
  1516. {
  1517. if(m_punkParent)
  1518. {
  1519. // Get the globe spinning indicating our processing
  1520. hr = IUnknown_QueryServiceExec(m_punkParent, SID_SBrandBand, &CGID_BrandCmdGroup, CBRANDIDM_STARTGLOBEANIMATION, 0, NULL, NULL);
  1521. }
  1522. m_pAsyncNav->_dwParseFlags = dwParseFlags;
  1523. m_pAsyncNav->_hwnd = m_hwnd;
  1524. if (pszUrl)
  1525. Str_SetPtr(&(m_pAsyncNav->_pszUrl), pszUrl);
  1526. else
  1527. m_pAsyncNav->_fPidlCheckOnly = TRUE;
  1528. if (!pszUrl || (pszUrl && m_pAsyncNav->_pszUrl))
  1529. {
  1530. CShellUrl *pshu = new CShellUrl();
  1531. if (pshu)
  1532. {
  1533. hr = pshu->Clone(m_pshuUrl);
  1534. m_pAsyncNav->_pShellUrl = pshu;
  1535. // AddRef here to give it to the thread
  1536. m_pAsyncNav->AddRef();
  1537. // Create the thread that will do the PIDL creation
  1538. if (FAILED(hr) || !SHCreateThread(_AsyncNavigateThreadProc, (LPVOID)m_pAsyncNav, CTF_COINIT, NULL))
  1539. {
  1540. hr = E_FAIL;
  1541. }
  1542. else
  1543. {
  1544. hr = E_PENDING;
  1545. }
  1546. }
  1547. }
  1548. }
  1549. }
  1550. if (FAILED(hr) && hr != E_PENDING)
  1551. {
  1552. // Cancel Async navigation leftovers
  1553. _CancelNavigation();
  1554. if (pszUrl)
  1555. {
  1556. BOOL fWasCorrected = FALSE;
  1557. hr = m_pshuUrl->ParseFromOutsideSource(pszUrl, dwParseFlags, &fWasCorrected);
  1558. // If the URL was autocorrected, put the corrected url in the editbox
  1559. // so that an invalid url in not added to our MRU if navigation succeeds
  1560. if (SUCCEEDED(hr) && fWasCorrected)
  1561. {
  1562. if (SUCCEEDED(m_pshuUrl->GetUrl(szDisplayName, ARRAYSIZE(szDisplayName))))
  1563. {
  1564. SetWindowText(m_hwndEdit, szDisplayName);
  1565. }
  1566. }
  1567. }
  1568. }
  1569. }
  1570. }
  1571. if (SUCCEEDED(hr))
  1572. {
  1573. _FinishNavigate();
  1574. }
  1575. SetCursor(hCursorOld);
  1576. return hr;
  1577. }
  1578. HRESULT CAddressEditBox::_FinishNavigate()
  1579. {
  1580. HRESULT hr;
  1581. hr = Execute( (m_fConnectedToBrowser ? SHURL_EXECFLAGS_NONE : SHURL_EXECFLAGS_DONTFORCEIE));
  1582. // if we managed to navigate by one means or another, then do all the
  1583. // associated processing
  1584. if (SUCCEEDED(hr))
  1585. {
  1586. TCHAR szDisplayName[MAX_URL_STRING];
  1587. hr = m_pshuUrl->GetDisplayName(szDisplayName, SIZECHARS(szDisplayName));
  1588. ASSERT(SUCCEEDED(hr));
  1589. Str_SetPtr(&m_pszPendingURL, szDisplayName);
  1590. if (!m_fConnectedToBrowser || m_fDidShellExec)
  1591. {
  1592. // We aren't connected to a browser window
  1593. // so we need to call _NavigationComplete() our selves
  1594. // because it will not come from the Browser window
  1595. // it self.
  1596. // If m_fDidShellExec, we need to manually add this because
  1597. // we won't receive a DISPID_NAVIGATECOMPLETE event, but
  1598. // we pass NULL to indicate
  1599. hr = _NavigationComplete(szDisplayName, !m_fDidShellExec, TRUE);
  1600. }
  1601. }
  1602. return hr;
  1603. }
  1604. void CAddressEditBox::_JustifyAddressBarText( void )
  1605. {
  1606. // Either of the following appear to work:
  1607. // (a) EM_SETSEL(0,0) followed by EM_SCROLLCARET(0,0)
  1608. // SendMessage( m_hwndEdit, EM_SETSEL, 0, 0 );
  1609. // SendMessage( m_hwndEdit, EM_SCROLLCARET, 0, 0 );
  1610. // (b) WM_KEYDOWN with VK_HOME
  1611. // SendMessage( m_hwndEdit, WM_KEYDOWN, VK_HOME, 0 );
  1612. // Use the EM_SETSEL method to avoid user keyboard stroke interruption.
  1613. SendMessage( m_hwndEdit, EM_SETSEL, 0, 0 );
  1614. SendMessage( m_hwndEdit, EM_SCROLLCARET, 0, 0 );
  1615. }
  1616. HRESULT CAddressEditBox::_AsyncNavigate(AsyncNav *pAsyncNav)
  1617. {
  1618. HRESULT hr;
  1619. // we should only be called on one thread, but the interlocked can't hurt...
  1620. if (InterlockedCompareExchange((LONG*)&m_fAsyncNavInProgress, TRUE, FALSE) == FALSE)
  1621. {
  1622. // this is the first call to _AsyncNavigate
  1623. hr = pAsyncNav->_hr;
  1624. if (SUCCEEDED(hr))
  1625. {
  1626. // Get the CShellUrl back after processing
  1627. hr = m_pshuUrl->Clone(pAsyncNav->_pShellUrl);
  1628. }
  1629. // If the URL was autocorrected, put the corrected url in the editbox
  1630. // so that an invalid url in not added to our MRU if navigation succeeds
  1631. if (SUCCEEDED(hr) && pAsyncNav->_fWasCorrected)
  1632. {
  1633. TCHAR szDisplayName[MAX_URL_STRING];
  1634. if (SUCCEEDED(m_pshuUrl->GetUrl(szDisplayName, ARRAYSIZE(szDisplayName))))
  1635. {
  1636. SetWindowText(m_hwndEdit, szDisplayName);
  1637. }
  1638. }
  1639. if (SUCCEEDED(hr))
  1640. hr = _FinishNavigate();
  1641. if (FAILED(hr) && pAsyncNav->_fPidlCheckOnly)
  1642. {
  1643. // Maybe the user needs to insert the media, format, or
  1644. // reconnect to the disk before this will succeed. Check for that
  1645. // and prompt now.
  1646. // This fixes the common case where the floppy or CD isn't inserted and
  1647. // we want to display the user friendly dialog.
  1648. LPITEMIDLIST pidl;
  1649. hr = pAsyncNav->_pShellUrl->GetPidlNoGenerate(&pidl);
  1650. // We need to resolve the URL into its path so SHPathPrepareForWrite works correctly
  1651. if (SUCCEEDED(hr))
  1652. {
  1653. TCHAR szShortcutFilePath[MAX_PATH];
  1654. hr = IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szShortcutFilePath, SIZECHARS(szShortcutFilePath), NULL);
  1655. if (SUCCEEDED(hr))
  1656. {
  1657. HRESULT hrPrompt = SHPathPrepareForWrite(pAsyncNav->_hwnd, NULL, szShortcutFilePath, SHPPFW_DEFAULT);
  1658. if (SUCCEEDED(hrPrompt))
  1659. {
  1660. hr = _FinishNavigate();
  1661. }
  1662. else
  1663. {
  1664. // Propagate out the fact that the user clicked the cancel button.
  1665. hr = hrPrompt;
  1666. }
  1667. }
  1668. ILFree(pidl);
  1669. }
  1670. // Never display a err if the user cancelled the operation.
  1671. if (FAILED(hr) && (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr))
  1672. {
  1673. TCHAR szDisplayName[MAX_URL_STRING];
  1674. if (SUCCEEDED(pAsyncNav->_pShellUrl->GetUrl(szDisplayName, ARRAYSIZE(szDisplayName))))
  1675. {
  1676. MLShellMessageBox(pAsyncNav->_hwnd, MAKEINTRESOURCE(IDS_ADDRBAND_DEVICE_NOTAVAILABLE),
  1677. MAKEINTRESOURCE(IDS_SHURL_ERR_TITLE),
  1678. (MB_OK | MB_ICONERROR), szDisplayName);
  1679. }
  1680. }
  1681. }
  1682. // Cleanup async navigation stuff
  1683. _CancelNavigation();
  1684. InterlockedExchange((LONG*)&m_fAsyncNavInProgress, FALSE);
  1685. }
  1686. else
  1687. {
  1688. // we can only do one async navigate at a time
  1689. hr = E_FAIL;
  1690. }
  1691. return hr;
  1692. }
  1693. HRESULT CAddressEditBox::_CancelNavigation()
  1694. {
  1695. if (m_pAsyncNav)
  1696. {
  1697. if(m_punkParent)
  1698. {
  1699. HRESULT hr = IUnknown_QueryServiceExec(m_punkParent, SID_SBrandBand, &CGID_BrandCmdGroup, CBRANDIDM_STOPGLOBEANIMATION, 0, NULL, NULL);
  1700. }
  1701. m_pAsyncNav->SetCanceledFlag();
  1702. m_pAsyncNav->Release();
  1703. m_pAsyncNav = NULL;
  1704. }
  1705. return S_OK;
  1706. }
  1707. DWORD CAddressEditBox::_AsyncNavigateThreadProc(LPVOID pvData)
  1708. {
  1709. AsyncNav *pAsyncNav = (AsyncNav *)pvData;
  1710. if (pAsyncNav->_hwnd && g_nAEB_AsyncNavigation)
  1711. {
  1712. if(pAsyncNav->_fPidlCheckOnly)
  1713. {
  1714. LPITEMIDLIST pidl;
  1715. pAsyncNav->_hr = pAsyncNav->_pShellUrl->GetPidlNoGenerate(&pidl);
  1716. if (SUCCEEDED(pAsyncNav->_hr))
  1717. {
  1718. DWORD dwAttrib = SFGAO_VALIDATE;
  1719. pAsyncNav->_hr = IEGetNameAndFlags(pidl, 0, NULL, 0, &dwAttrib);
  1720. }
  1721. else
  1722. {
  1723. // Special case for keywords. We want to proceed if we don't have a pidl
  1724. pAsyncNav->_hr = S_OK;
  1725. }
  1726. }
  1727. else
  1728. {
  1729. pAsyncNav->_hr = pAsyncNav->_pShellUrl->ParseFromOutsideSource(pAsyncNav->_pszUrl, pAsyncNav->_dwParseFlags, &(pAsyncNav->_fWasCorrected), &(pAsyncNav->_fWasCanceled));
  1730. }
  1731. pAsyncNav->_fReady = TRUE;
  1732. PostMessage(pAsyncNav->_hwnd, g_nAEB_AsyncNavigation, (WPARAM)pAsyncNav, NULL);
  1733. }
  1734. // We are done with this now.
  1735. // If the navigation was canceled, then the object will destruct now, and the posted
  1736. // message above will be ignored.
  1737. pAsyncNav->Release();
  1738. return 0;
  1739. }
  1740. BOOL CAddressEditBox::_IsShellUrl(void)
  1741. {
  1742. // 1. Check if we need to change the List.
  1743. BOOL fIsShellUrl = !m_pshuUrl->IsWebUrl();
  1744. if (fIsShellUrl)
  1745. {
  1746. // BUG #50703: Users want MRU when about: url is displayed.
  1747. TCHAR szUrl[MAX_URL_STRING];
  1748. if (SUCCEEDED(m_pshuUrl->GetUrl(szUrl, ARRAYSIZE(szUrl))))
  1749. {
  1750. if (URL_SCHEME_ABOUT == GetUrlScheme(szUrl))
  1751. {
  1752. fIsShellUrl = FALSE; // Make it use the MRU List.
  1753. }
  1754. }
  1755. }
  1756. return fIsShellUrl;
  1757. }
  1758. /*******************************************************************
  1759. FUNCTION: _NavigationComplete
  1760. PARAMETERS:
  1761. pszUrl - String user entered.
  1762. fChangeLists - Should we modify the Drop Down list?
  1763. fAddToMRU - Should we add it to the MRU?
  1764. DESCRIPTION:
  1765. This function is called when either: 1) a naviation completes,
  1766. or 2) the user entered text into the AddressEditBox that needs
  1767. to be handled but will not cause a NAVIGATION_COMPLETE message.
  1768. This function will change the AddressList being used and will
  1769. add the item to the Type-in MRU.
  1770. ********************************************************************/
  1771. HRESULT CAddressEditBox::_NavigationComplete(LPCTSTR pszUrl /* Optional */, BOOL fChangeLists, BOOL fAddToMRU)
  1772. {
  1773. HRESULT hr = S_OK;
  1774. // Are we controlling a ComboBoxEx?
  1775. if (m_hwnd)
  1776. {
  1777. // Yes, so do ComboBoxEx Specific things...
  1778. // If the list is dropped, undrop it so the contents of the editbox and list
  1779. // are properly updated.
  1780. if (m_hwnd && m_hwndEdit && ComboBox_GetDroppedState(m_hwnd))
  1781. {
  1782. SendMessage(m_hwndEdit, WM_KEYDOWN, VK_ESCAPE, 0);
  1783. }
  1784. if (fChangeLists)
  1785. {
  1786. BOOL fIsShellUrl = _IsShellUrl();
  1787. // 2. Do we need to change lists to MRU List?
  1788. if (!fIsShellUrl && m_elt != LT_TYPEIN_MRU)
  1789. {
  1790. // We need to start using the LT_TYPEIN_MRU list
  1791. // because that list is what is needed for Internet Urls.
  1792. _UseNewList(LT_TYPEIN_MRU);
  1793. }
  1794. // We only want to switch to using the shell name space
  1795. // if we are connected to a browser.
  1796. if (fIsShellUrl && (m_elt != LT_SHELLNAMESPACE) && m_fConnectedToBrowser)
  1797. {
  1798. // We need to start using the LT_SHELLNAMESPACE list
  1799. // because that list is what is needed for File Urls.
  1800. _UseNewList(LT_SHELLNAMESPACE);
  1801. }
  1802. ASSERT(m_palCurrent);
  1803. hr = m_palCurrent ? m_palCurrent->NavigationComplete((LPVOID) m_pshuUrl) : E_FAIL;
  1804. if ( SUCCEEDED( hr ) )
  1805. {
  1806. // Insure that after the navigation completes, the Address Bar Text is left justified.
  1807. _JustifyAddressBarText();
  1808. }
  1809. }
  1810. // Don't display the url to internal error pages. All internal error
  1811. // urls start with res:// and we don't want these in our MRU.
  1812. // We also don't want to display error pages from the server.
  1813. if ((pszUrl && (TEXT('r') == pszUrl[0]) && (TEXT('e') == pszUrl[1]) && IsErrorUrl(pszUrl)) ||
  1814. (m_pszHttpErrorUrl && StrCmp(m_pszHttpErrorUrl, pszUrl) == 0))
  1815. {
  1816. // We don't want this in our MRU!
  1817. fAddToMRU = FALSE;
  1818. }
  1819. // Do we have a Pending URL, meaning the user hand typed it in
  1820. // and the navigation finished (wasn't cancelled or failed).
  1821. //
  1822. // REARCHITECT: Currently there are a few cases when the URL (m_pszPendingURL)
  1823. // is added to the MRU when it shouldn't.
  1824. // 1. If the user enters an URL and then cancels the navigation, we
  1825. // don't clear m_pszPendingURL. If the user then causes the browser
  1826. // to navigate by some other means (HREF Click, Favorites/QLink navigation
  1827. // , or Floating AddressBand), we will receive the NAVIGATION_COMPLETE
  1828. // message and think it was for the originally cancelled URL.
  1829. if (fAddToMRU && m_pszPendingURL)
  1830. {
  1831. // Yes, so add it to the MRU.
  1832. if (SUCCEEDED(hr))
  1833. {
  1834. if (!m_pmru && m_palMRU)
  1835. hr = m_palMRU->QueryInterface(IID_IMRU, (LPVOID *)&m_pmru);
  1836. if (SUCCEEDED(hr))
  1837. {
  1838. SHCleanupUrlForDisplay(m_pszPendingURL);
  1839. hr = m_pmru->AddEntry(m_pszPendingURL); // Add to MRU
  1840. }
  1841. }
  1842. }
  1843. }
  1844. Str_SetPtr(&m_pszPendingURL, NULL);
  1845. Str_SetPtr(&m_pszHttpErrorUrl, NULL);
  1846. return hr;
  1847. }
  1848. //=================================================================
  1849. // AddressEditBox Modification Functions
  1850. //=================================================================
  1851. /****************************************************\
  1852. _ComboSubclassWndProc
  1853. Input:
  1854. Standard WndProc parameters
  1855. Return:
  1856. Standard WndProc return.
  1857. \****************************************************/
  1858. LRESULT CALLBACK CAddressEditBox::_ComboSubclassWndProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
  1859. {
  1860. HWND hwndBand = GetParent(hwnd);
  1861. CAddressEditBox * paeb = (CAddressEditBox*)GetProp(hwnd, SZ_ADDRESSCOMBO_PROP);
  1862. ASSERT(paeb);
  1863. g_hWinStationAfter = GetProcessWindowStation();
  1864. // In stress we see someone will stomp our property with -2. We need to find out who it is.
  1865. // Call ReinerF if this happens
  1866. AssertMsg(((void *)-2 != paeb), TEXT("Someone corrupted our window property. Call ReinerF"));
  1867. if (!paeb)
  1868. {
  1869. return DefWindowProcWrap(hwnd, uMessage, wParam, lParam);
  1870. }
  1871. switch (uMessage)
  1872. {
  1873. case WM_SETCURSOR:
  1874. {
  1875. HWND hwndCursor = (HWND)wParam;
  1876. int nHittest = LOWORD(lParam);
  1877. if (hwndCursor == paeb->m_hwndEdit && nHittest == HTCLIENT)
  1878. {
  1879. //
  1880. // If we don't have focus, we want to show an arrow because clicking will select
  1881. // the contents of the edit box. Otherwise show the I-beam. Also, if the edit box
  1882. // is empty we show the I-beam because there is nothing to select.
  1883. //
  1884. HWND hwndFocus = GetFocus();
  1885. int cch = GetWindowTextLength(paeb->m_hwndEdit);
  1886. LPCTSTR lpCursorName = (cch == 0 || hwndFocus == paeb->m_hwndEdit) ? IDC_IBEAM : IDC_ARROW;
  1887. SetCursor(LoadCursor(NULL, lpCursorName));
  1888. return TRUE;
  1889. }
  1890. break;
  1891. }
  1892. case WM_SETFOCUS:
  1893. //
  1894. // This is gross, but if the window was destroyed that had the
  1895. // focus this would fail and we would not get this to the
  1896. // combo box.
  1897. //
  1898. // This happens if you click on the combobox while
  1899. // renaming a file in the defview.
  1900. //
  1901. if (wParam && !IsWindow((HWND)wParam))
  1902. wParam = 0;
  1903. break;
  1904. case WM_DESTROY:
  1905. // Unsubclass myself.
  1906. if (!paeb->m_lpfnComboWndProc)
  1907. return 0;
  1908. SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) paeb->m_lpfnComboWndProc);
  1909. RemoveProp(hwnd, SZ_ADDRESSCOMBO_PROP);
  1910. ASSERT(paeb->m_hwnd); // We don't want to be called twice
  1911. paeb->m_hwnd = NULL; // We have been destroyed.
  1912. break;
  1913. case WM_COMMAND:
  1914. if (EN_UPDATE == GET_WM_COMMAND_CMD(wParam, lParam))
  1915. {
  1916. paeb->_InstallHookIfDirty();
  1917. }
  1918. break;
  1919. case WM_KEYDOWN:
  1920. switch (wParam)
  1921. {
  1922. //
  1923. // Pressing escape results in the dropdown being hidden. If
  1924. // the mouse hot-tracks over a different selection than when the
  1925. // combo was first dropped, we get a CBN_SELCHANGE event which
  1926. // causes a false navigation. We suppress this by setting
  1927. // m_nOldSelection to a special value (-2).
  1928. //
  1929. case VK_ESCAPE:
  1930. {
  1931. paeb->m_nOldSelection = SEL_ESCAPE_PRESSED;
  1932. // Pass message on so that the content of the edit box is restored
  1933. SendMessage(paeb->m_hwndEdit, uMessage, wParam, lParam);
  1934. break;
  1935. }
  1936. }
  1937. break;
  1938. case WM_SYSKEYDOWN:
  1939. switch (wParam)
  1940. {
  1941. case VK_DOWN:
  1942. {
  1943. // Alt-down toggles the combobox dropdown. We don't
  1944. // want a navigation if this key sequence closes the dropdown.
  1945. if (HIWORD(lParam) & KF_ALTDOWN)
  1946. {
  1947. paeb->m_nOldSelection = SEL_ESCAPE_PRESSED;
  1948. }
  1949. break;
  1950. }
  1951. }
  1952. break;
  1953. case CB_SHOWDROPDOWN:
  1954. // If dropdown is hidden, suppress navigation. See comment above for VK_ESCAPE.
  1955. if (!wParam)
  1956. {
  1957. paeb->m_nOldSelection = SEL_ESCAPE_PRESSED;
  1958. }
  1959. break;
  1960. case WM_WINDOWPOSCHANGING:
  1961. {
  1962. LPWINDOWPOS pwp = (LPWINDOWPOS)lParam;
  1963. pwp->flags |= SWP_NOCOPYBITS;
  1964. }
  1965. break;
  1966. case WM_GETOBJECT:
  1967. if ((DWORD)lParam == OBJID_CLIENT)
  1968. {
  1969. CAddressEditAccessible *paea = new CAddressEditAccessible(hwnd, paeb->m_hwndEdit);
  1970. if (NULL != paea)
  1971. {
  1972. LRESULT lres = LresultFromObject(IID_IAccessible, wParam, SAFECAST(paea, IAccessible *));
  1973. paea->Release();
  1974. return lres;
  1975. }
  1976. }
  1977. break;
  1978. default:
  1979. // FEATURE: Do we need this?
  1980. if (!(AEB_INIT_SUBCLASS & paeb->m_dwFlags))
  1981. {
  1982. paeb->OnWinEvent(paeb->m_hwnd, uMessage, wParam, lParam, NULL);
  1983. }
  1984. break;
  1985. }
  1986. return CallWindowProc(paeb->m_lpfnComboWndProc, hwnd, uMessage, wParam, lParam);
  1987. }
  1988. void CAddressEditBox::_SetAutocompleteOptions()
  1989. {
  1990. if (m_pac)
  1991. {
  1992. // Set the autocomplete options
  1993. DWORD dwOptions = ACO_SEARCH | ACO_FILTERPREFIXES | ACO_USETAB | ACO_UPDOWNKEYDROPSLIST;
  1994. if (SHRegGetBoolUSValue(REGSTR_PATH_AUTOCOMPLETE, REGSTR_VAL_USEAUTOAPPEND, FALSE, /*default:*/FALSE))
  1995. {
  1996. dwOptions |= ACO_AUTOAPPEND;
  1997. }
  1998. if (SHRegGetBoolUSValue(REGSTR_PATH_AUTOCOMPLETE, REGSTR_VAL_USEAUTOSUGGEST, FALSE, /*default:*/TRUE))
  1999. {
  2000. dwOptions |= ACO_AUTOSUGGEST;
  2001. }
  2002. m_pac->SetOptions(dwOptions);
  2003. }
  2004. }
  2005. /****************************************************\
  2006. FUNCTION: _NavigateToUrlCB
  2007. PARAMETERS:
  2008. lParam - The CAddressEditBox this pointer.
  2009. lpUrl - The URL to navigate to.
  2010. DESCRIPTION:
  2011. This function is specifically for AutoComplete
  2012. to call when it needs to navigate.
  2013. \****************************************************/
  2014. HRESULT CAddressEditBox::_NavigateToUrlCB(LPARAM lParam, LPTSTR lpUrl)
  2015. {
  2016. // NOTE: We don't need to navigate because AutoComplete will
  2017. // will send a message to the ComboBoxEx that will carry out
  2018. // the navigation.
  2019. return S_OK;
  2020. }
  2021. //=================================================================
  2022. // Functions to prevent clobbering the address contents while dirty
  2023. //=================================================================
  2024. #define TF_EDITBOX TF_BAND|TF_GENERAL
  2025. //#define TF_EDITBOX TF_ALWAYS
  2026. BOOL CAddressEditBox::_IsDirty()
  2027. {
  2028. return m_hwndEdit && SendMessage(m_hwndEdit, EM_GETMODIFY, 0, 0L);
  2029. }
  2030. void CAddressEditBox::_ClearDirtyFlag()
  2031. {
  2032. TraceMsg(TF_EDITBOX, "CAddressEditBox::_ClearDirtyFlag()");
  2033. SendMessage(m_hwndEdit, EM_SETMODIFY, FALSE, 0);
  2034. _RemoveHook();
  2035. }
  2036. void CAddressEditBox::_InstallHookIfDirty()
  2037. {
  2038. //
  2039. // We only need to install the hook if we are connected to a browser for update notifications
  2040. //
  2041. if (m_fConnectedToBrowser)
  2042. {
  2043. // Make sure we are associated with the current thread
  2044. if (!m_fAssociated)
  2045. {
  2046. //
  2047. // If a CAddressEditBox is already associated with this thread, remove that
  2048. // association and remove any pending mouse hook. This can happen if the
  2049. // open dialog comes up and the address bar is visible.
  2050. //
  2051. DWORD dwThread = GetCurrentThreadId();
  2052. CAddressEditBox* pAeb;
  2053. if (SUCCEEDED(m_al.Find(dwThread, (LPVOID*)&pAeb)))
  2054. {
  2055. pAeb->_ClearDirtyFlag();
  2056. pAeb->m_fAssociated = FALSE;
  2057. m_al.Delete(dwThread);
  2058. }
  2059. // There should not be any other CAddressEditBox associated with this thread!
  2060. ASSERT(FAILED(m_al.Find(dwThread, (LPVOID*)&pAeb)));
  2061. //
  2062. // Associate ourselves with the current thread id. We need this because
  2063. // windows hooks are global and have no data associated with them.
  2064. // On the callback, we use our thread id as the key.
  2065. //
  2066. m_al.Add(dwThread, this);
  2067. m_fAssociated = TRUE;
  2068. }
  2069. if (!m_hhook && _IsDirty())
  2070. {
  2071. // ML: HINST_THISDLL is valid in its use here
  2072. m_hhook = SetWindowsHookEx(WH_MOUSE, _MsgHook, HINST_THISDLL, GetCurrentThreadId());
  2073. TraceMsg(TF_EDITBOX, "CAddressEditBox::_InstallHookIfDirty(), Hook installed");
  2074. //
  2075. // Subclass edit control of the combobox. We do this here rather than when this
  2076. // class is initialized so that we are first in the chain to receive messages.
  2077. //
  2078. if (!m_lpfnEditWndProc && m_hwndEdit && SetProp(m_hwndEdit, SZ_ADDRESSCOMBO_PROP, this))
  2079. {
  2080. m_lpfnEditWndProc = (WNDPROC)SetWindowLongPtr(m_hwndEdit, GWLP_WNDPROC, (LONG_PTR) _EditSubclassWndProc);
  2081. }
  2082. // Clear and changes that we previously cached
  2083. m_cbex.mask = 0;
  2084. }
  2085. }
  2086. }
  2087. void CAddressEditBox::_RemoveHook()
  2088. {
  2089. if (m_hhook)
  2090. {
  2091. UnhookWindowsHookEx(m_hhook);
  2092. m_hhook = FALSE;
  2093. TraceMsg(TF_EDITBOX, "CAddressEditBox::_RemoveHook(), Hook removed");
  2094. }
  2095. }
  2096. LRESULT CALLBACK CAddressEditBox::_MsgHook(int nCode, WPARAM wParam, LPARAM lParam)
  2097. {
  2098. //
  2099. // Get the CAddressEditBox associated with this thread. We need this because
  2100. // windows hooks are global and have no data associated with them.
  2101. // On the callback, we use our thread id as the key
  2102. //
  2103. CAddressEditBox* pThis;
  2104. if (SUCCEEDED(CAddressEditBox::m_al.Find(GetCurrentThreadId(), (LPVOID*)&pThis)))
  2105. {
  2106. return pThis->_MsgHook(nCode, wParam, (MOUSEHOOKSTRUCT*)lParam);
  2107. }
  2108. return 0;
  2109. }
  2110. LRESULT CAddressEditBox::_MsgHook(int nCode, WPARAM wParam, MOUSEHOOKSTRUCT *pmhs)
  2111. {
  2112. ASSERT(NULL != pmhs);
  2113. if (nCode >= 0)
  2114. {
  2115. if ((wParam == WM_LBUTTONDOWN) || (wParam == WM_RBUTTONDOWN))
  2116. {
  2117. // Ignore if the button was clicked in our combo box
  2118. RECT rc;
  2119. if (GetWindowRect(m_hwnd, &rc) && !PtInRect(&rc, pmhs->pt))
  2120. {
  2121. _ClearDirtyFlag();
  2122. _RemoveHook();
  2123. }
  2124. }
  2125. }
  2126. return CallNextHookEx(m_hhook, nCode, wParam, (LPARAM)pmhs);
  2127. }
  2128. /****************************************************\
  2129. _ComboExSubclassWndProc
  2130. Input:
  2131. Standard WndProc parameters
  2132. Return:
  2133. Standard WndProc return.
  2134. Description:
  2135. We subclass the outer combobox to prevent
  2136. the contents from getting clobbered while
  2137. and edit is in progress (ie dirty).
  2138. \****************************************************/
  2139. LRESULT CALLBACK CAddressEditBox::_ComboExSubclassWndProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
  2140. {
  2141. CAddressEditBox * paeb = (CAddressEditBox*)GetProp(hwnd, SZ_ADDRESSCOMBOEX_PROP);
  2142. if (!paeb)
  2143. return DefWindowProc(hwnd, uMessage, wParam, lParam);
  2144. g_hWinStationAfterEx = GetProcessWindowStation();
  2145. if (uMessage == g_nAEB_AsyncNavigation)
  2146. {
  2147. // If the navigation was not canceled before, then navigate now.
  2148. if ((AsyncNav *)wParam == paeb->m_pAsyncNav && paeb->m_pAsyncNav->_fReady)
  2149. {
  2150. paeb->_AsyncNavigate((AsyncNav *)wParam);
  2151. }
  2152. }
  2153. switch (uMessage)
  2154. {
  2155. case CBEM_SETITEM:
  2156. {
  2157. //
  2158. // If we are still dirty, don't let anyone clobber our edit control contents!
  2159. //
  2160. const COMBOBOXEXITEM* pcCBItem = (const COMBOBOXEXITEM FAR *)lParam;
  2161. if (paeb->_IsDirty() && pcCBItem->iItem == -1)
  2162. {
  2163. //
  2164. // save this info so that if the user hits esc, we restore the right thing
  2165. //
  2166. if (IsFlagSet(pcCBItem->mask, CBEIF_TEXT))
  2167. {
  2168. Str_SetPtr(&paeb->m_pszCurrentUrl, pcCBItem->pszText);
  2169. }
  2170. Str_SetPtr(&(paeb->m_cbex.pszText), NULL); // Free the previous value
  2171. paeb->m_cbex = *pcCBItem;
  2172. paeb->m_cbex.pszText = NULL;
  2173. Str_SetPtr(&(paeb->m_cbex.pszText), paeb->m_pszCurrentUrl);
  2174. paeb->m_cbex.cchTextMax = lstrlen(paeb->m_cbex.pszText);
  2175. return 0L;
  2176. }
  2177. else
  2178. {
  2179. // Make sure that the icon is visible
  2180. SendMessage(paeb->m_hwnd, CBEM_SETEXTENDEDSTYLE, CBES_EX_NOEDITIMAGE, 0);
  2181. }
  2182. }
  2183. break;
  2184. case WM_DESTROY:
  2185. // Release the lists now so that they don't try to use our
  2186. // window after we're destroyed
  2187. if (paeb->m_palCurrent)
  2188. {
  2189. paeb->m_palCurrent->Connect(FALSE, paeb->m_hwnd, NULL, NULL, NULL);
  2190. ATOMICRELEASE(paeb->m_palCurrent);
  2191. }
  2192. ATOMICRELEASE(paeb->m_palSNS);
  2193. ATOMICRELEASE(paeb->m_palMRU);
  2194. //
  2195. // Unsubclass myself.
  2196. //
  2197. RemoveProp(hwnd, SZ_ADDRESSCOMBOEX_PROP);
  2198. if (!paeb->m_lpfnComboExWndProc)
  2199. return 0;
  2200. SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) paeb->m_lpfnComboExWndProc);
  2201. break;
  2202. default:
  2203. break;
  2204. }
  2205. return CallWindowProc(paeb->m_lpfnComboExWndProc, hwnd, uMessage, wParam, lParam);
  2206. }
  2207. /****************************************************\
  2208. _EnumFindWindow
  2209. Description:
  2210. Called by EnumChildWindows to see is the window
  2211. passed in lParam is a child of a given
  2212. parent.
  2213. \****************************************************/
  2214. BOOL CALLBACK CAddressEditBox::_EnumFindWindow
  2215. (
  2216. HWND hwnd, // handle to child window
  2217. LPARAM lParam // application-defined value
  2218. )
  2219. {
  2220. // Stop enumeration when match found
  2221. return (hwnd != (HWND)lParam);
  2222. }
  2223. /****************************************************\
  2224. _EditSubclassWndProc
  2225. Input:
  2226. Standard WndProc parameters
  2227. Return:
  2228. Standard WndProc return.
  2229. Description:
  2230. We subclass the edit control in the combobox
  2231. so that we can keep it from losing focus under
  2232. certain conditions.
  2233. \****************************************************/
  2234. LRESULT CALLBACK CAddressEditBox::_EditSubclassWndProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
  2235. {
  2236. CAddressEditBox * paeb = (CAddressEditBox*)GetProp(hwnd, SZ_ADDRESSCOMBO_PROP);
  2237. if (!paeb)
  2238. return DefWindowProc(hwnd, uMessage, wParam, lParam);
  2239. switch (uMessage)
  2240. {
  2241. case WM_SETCURSOR:
  2242. {
  2243. HWND hwndCursor = (HWND)wParam;
  2244. int nHittest = LOWORD(lParam);
  2245. if (hwndCursor == hwnd && nHittest == HTCLIENT)
  2246. {
  2247. //
  2248. // If we don't have focus, we want to show an arrow because clicking will select
  2249. // the contents of the edit box. Otherwise show the I-beam. Also, if the edit box
  2250. // is empty we show the I-beam because there is nothing to select.
  2251. //
  2252. int cch = GetWindowTextLength(paeb->m_hwndEdit);
  2253. LPCTSTR lpCursorName = (cch == 0 || GetFocus() == hwnd) ? IDC_IBEAM : IDC_ARROW;
  2254. SetCursor(LoadCursor(NULL, lpCursorName));
  2255. return TRUE;
  2256. }
  2257. break;
  2258. }
  2259. case WM_KILLFOCUS:
  2260. {
  2261. //
  2262. // If we lose focus with the mouse hook installed, the user probably did
  2263. // not initiate the change so we try to grab it back. The hook is removed
  2264. // when the user clicks outside the edit box or presses a key to finish the edit
  2265. // (tab, enter, or esc)
  2266. //
  2267. HWND hwndGetFocus = (HWND)wParam;
  2268. if ((paeb->m_hhook) && hwndGetFocus && (hwnd != hwndGetFocus))
  2269. {
  2270. //
  2271. // Make sure that this is not the drop-down portion of the combo.
  2272. // Also, if we are in a dialog (open dialog) then we don't see the
  2273. // tab key. So if focus is going to a sibling we'll let it through.
  2274. //
  2275. HWND hwndGetFocusParent = GetParent(hwndGetFocus);
  2276. HWND hwndSiblingParent = paeb->m_hwnd ? GetParent(paeb->m_hwnd) : GetParent(hwnd);
  2277. if ((paeb->m_hwnd != hwndGetFocusParent) && (hwndGetFocusParent != hwndSiblingParent) &&
  2278. EnumChildWindows(hwndSiblingParent, _EnumFindWindow, (LPARAM)hwndGetFocus))
  2279. {
  2280. // Get the top-level window of who's getting focus
  2281. HWND hwndFrame = hwndGetFocus;
  2282. HWND hwndParent;
  2283. while (hwndParent = GetParent(hwndFrame))
  2284. hwndFrame = hwndParent;
  2285. // If focus is going somewhere else in our browser window, grab focus back
  2286. if (hwndFrame == paeb->m_hwndBrowser)
  2287. {
  2288. DWORD dwStart, dwEnd;
  2289. SendMessage(paeb->m_hwndEdit, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
  2290. SetFocus(paeb->m_hwndEdit);
  2291. SendMessage(paeb->m_hwndEdit, EM_SETSEL, dwStart, dwEnd);
  2292. TraceMsg(TF_BAND|TF_GENERAL, "CAddressEditBox::_EditSubclassWndProc, Restoring focus");
  2293. return 0L;
  2294. }
  2295. }
  2296. }
  2297. //
  2298. // Losing focus so allow others to change our contents
  2299. //
  2300. paeb->_ClearDirtyFlag();
  2301. }
  2302. break;
  2303. case WM_KEYDOWN:
  2304. {
  2305. // If we are tabbing away, clear our dirty flag
  2306. switch (wParam)
  2307. {
  2308. case VK_TAB:
  2309. paeb->_ClearDirtyFlag();
  2310. break;
  2311. case VK_ESCAPE:
  2312. {
  2313. if (paeb->m_hwnd && ComboBox_GetDroppedState(paeb->m_hwnd))
  2314. {
  2315. SendMessage(paeb->m_hwnd, CB_SHOWDROPDOWN, FALSE, 0);
  2316. }
  2317. else
  2318. {
  2319. IUnknown *punk = NULL;
  2320. if (paeb->m_pbp)
  2321. {
  2322. paeb->m_pbp->GetBrowserWindow(&punk);
  2323. }
  2324. if (punk)
  2325. {
  2326. IWebBrowser* pwb;
  2327. punk->QueryInterface(IID_IWebBrowser, (LPVOID*)&pwb);
  2328. if (pwb)
  2329. {
  2330. pwb->Stop();
  2331. pwb->Release();
  2332. }
  2333. punk->Release();
  2334. }
  2335. // Cancel pending navigation, if any
  2336. paeb->_CancelNavigation();
  2337. }
  2338. LRESULT lResult = CallWindowProc(paeb->m_lpfnEditWndProc, hwnd, uMessage, wParam, lParam);
  2339. // This bit of magic that restores the icon in the combobox. Otherwise when we
  2340. // dismiss the dropwown with escape we get the icon last selected in the dropdown.
  2341. HWND hwndCombo = (HWND)SendMessage(paeb->m_hwnd, CBEM_GETCOMBOCONTROL, 0, 0);
  2342. SendMessage(hwndCombo, CB_SETCURSEL, -1, 0);
  2343. return lResult;
  2344. }
  2345. }
  2346. break;
  2347. }
  2348. case WM_DESTROY:
  2349. // Unsubclass myself.
  2350. RemoveProp(hwnd, SZ_ADDRESSCOMBO_PROP);
  2351. if (!paeb->m_lpfnEditWndProc)
  2352. return 0;
  2353. SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) paeb->m_lpfnEditWndProc);
  2354. ASSERT(paeb->m_hwndEdit);
  2355. paeb->m_hwndEdit = NULL;
  2356. break;
  2357. default:
  2358. break;
  2359. }
  2360. return CallWindowProc(paeb->m_lpfnEditWndProc, hwnd, uMessage, wParam, lParam);
  2361. }
  2362. BOOL GetLabelStringW(HWND hwnd, LPWSTR pwszBuf, DWORD cchBuf)
  2363. {
  2364. HWND hwndLabel;
  2365. LONG lStyle;
  2366. LRESULT lResult;
  2367. BOOL result = FALSE;
  2368. ASSERT(pwszBuf && cchBuf);
  2369. *pwszBuf = 0;
  2370. if (IsWindow(hwnd))
  2371. {
  2372. hwndLabel = hwnd;
  2373. while (hwndLabel = GetWindow(hwndLabel, GW_HWNDPREV))
  2374. {
  2375. lStyle = GetWindowLong(hwndLabel, GWL_STYLE);
  2376. //
  2377. // Skip if invisible
  2378. //
  2379. if (!(lStyle & WS_VISIBLE))
  2380. continue;
  2381. //
  2382. // Is this a static dude?
  2383. //
  2384. lResult = SendMessage(hwndLabel, WM_GETDLGCODE, 0, 0);
  2385. if (lResult & DLGC_STATIC)
  2386. {
  2387. //
  2388. // Great, we've found our label.
  2389. //
  2390. result = GetWindowTextWrapW(hwndLabel, pwszBuf, cchBuf);
  2391. }
  2392. //
  2393. // Is this a tabstop or group? If so, bail out now.
  2394. //
  2395. if (lStyle & (WS_GROUP | WS_TABSTOP))
  2396. break;
  2397. }
  2398. }
  2399. return result;
  2400. }
  2401. CAddressEditAccessible::CAddressEditAccessible(HWND hwndCombo, HWND hwndEdit)
  2402. {
  2403. m_cRefCount = 1;
  2404. m_hwndEdit = hwndEdit;
  2405. WCHAR wszTitle[MAX_PATH];
  2406. if (!GetLabelStringW(GetParent(hwndCombo), wszTitle, ARRAYSIZE(wszTitle)))
  2407. {
  2408. MLLoadStringW(IDS_BAND_ADDRESS, wszTitle, ARRAYSIZE(wszTitle));
  2409. }
  2410. Str_SetPtr(&m_pwszName, wszTitle);
  2411. CreateStdAccessibleObject(hwndCombo, OBJID_CLIENT, IID_IAccessible, (void **)&m_pDelegateAccObj);
  2412. }
  2413. CAddressEditAccessible::~CAddressEditAccessible()
  2414. {
  2415. Str_SetPtr(&m_pwszName, NULL);
  2416. }
  2417. // *** IUnknown ***
  2418. STDMETHODIMP_(ULONG) CAddressEditAccessible::AddRef()
  2419. {
  2420. return InterlockedIncrement((LPLONG)&m_cRefCount);
  2421. }
  2422. STDMETHODIMP_(ULONG) CAddressEditAccessible::Release()
  2423. {
  2424. ASSERT( 0 != m_cRefCount );
  2425. ULONG cRef = InterlockedDecrement(&m_cRefCount);
  2426. if ( 0 == cRef )
  2427. {
  2428. delete this;
  2429. }
  2430. return cRef;
  2431. }
  2432. STDMETHODIMP CAddressEditAccessible::QueryInterface(REFIID riid, LPVOID * ppvObj)
  2433. {
  2434. return _DefQueryInterface(riid, ppvObj);
  2435. }
  2436. // *** IAccessible ***
  2437. STDMETHODIMP CAddressEditAccessible::get_accName(VARIANT varChild, BSTR *pszName)
  2438. {
  2439. *pszName = (m_pwszName != NULL) ? SysAllocString(m_pwszName) : NULL;
  2440. return (*pszName != NULL) ? S_OK : S_FALSE;
  2441. }
  2442. STDMETHODIMP CAddressEditAccessible::get_accValue(VARIANT varChild, BSTR *pszValue)
  2443. {
  2444. WCHAR wszValue[MAX_URL_STRING];
  2445. if (Edit_GetText(m_hwndEdit, wszValue, ARRAYSIZE(wszValue)))
  2446. {
  2447. *pszValue = SysAllocString(wszValue);
  2448. }
  2449. else
  2450. {
  2451. *pszValue = NULL;
  2452. }
  2453. return (*pszValue != NULL) ? S_OK : S_FALSE;
  2454. }