[BROWSEUI]
[reactos.git] / reactos / dll / win32 / browseui / addresseditbox.cpp
1 /*
2 * ReactOS Explorer
3 *
4 * Copyright 2009 Andrew Hill <ash77 at domain reactos.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 This class handles the combo box of the address band.
23 */
24
25 #include "precomp.h"
26
27 /*
28 TODO:
29 Handle listbox dropdown message and fill contents
30 Add drag and drop of icon in edit box
31 Handle change notifies to update appropriately
32 Fix so selection in combo listbox navigates
33 */
34
35 CAddressEditBox::CAddressEditBox() :
36 fCombobox(NULL, this, 1),
37 fEditWindow(NULL, this, 1),
38 fSite(NULL)
39 {
40 }
41
42 CAddressEditBox::~CAddressEditBox()
43 {
44 }
45
46 HRESULT STDMETHODCALLTYPE CAddressEditBox::SetOwner(IUnknown *)
47 {
48 // connect to browser connection point
49 return 0;
50 }
51
52 HRESULT STDMETHODCALLTYPE CAddressEditBox::FileSysChange(long param8, long paramC)
53 {
54 return E_NOTIMPL;
55 }
56
57 HRESULT STDMETHODCALLTYPE CAddressEditBox::Refresh(long param8)
58 {
59 return E_NOTIMPL;
60 }
61
62 HRESULT STDMETHODCALLTYPE CAddressEditBox::Init(HWND comboboxEx, HWND editControl, long param14, IUnknown *param18)
63 {
64 CComPtr<IBrowserService> browserService;
65
66 fCombobox.SubclassWindow(comboboxEx);
67 fEditWindow.SubclassWindow(editControl);
68 fSite = param18;
69
70 SHAutoComplete(fEditWindow.m_hWnd, SHACF_FILESYSTEM | SHACF_URLALL | SHACF_USETAB);
71
72 // take advice to watch events
73 HRESULT hResult = IUnknown_QueryService(param18, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &browserService));
74 if (SUCCEEDED(hResult))
75 {
76 hResult = AtlAdvise(browserService, static_cast<IDispatch *>(this), DIID_DWebBrowserEvents, &fAdviseCookie);
77 }
78
79 return hResult;
80 }
81
82 HRESULT STDMETHODCALLTYPE CAddressEditBox::SetCurrentDir(long paramC)
83 {
84 return E_NOTIMPL;
85 }
86
87 HRESULT STDMETHODCALLTYPE CAddressEditBox::ParseNow(long paramC)
88 {
89 ULONG eaten;
90 ULONG attributes;
91 HRESULT hr;
92 HWND topLevelWindow;
93
94 CComPtr<IShellBrowser> pisb;
95 hr = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &pisb));
96 if (FAILED_UNEXPECTEDLY(hr))
97 return hr;
98
99 hr = IUnknown_GetWindow(pisb, &topLevelWindow);
100 if (FAILED_UNEXPECTEDLY(hr))
101 return hr;
102
103 LPWSTR input;
104 int inputLength = fCombobox.GetWindowTextLength() + 2;
105
106 input = new WCHAR[inputLength];
107 fCombobox.GetWindowText(input, inputLength);
108
109 LPWSTR address;
110 int addressLength = ExpandEnvironmentStrings(input, NULL, 0);
111
112 if (addressLength <= 0)
113 {
114 address = input;
115 }
116 else
117 {
118 addressLength += 2;
119 address = new WCHAR[addressLength];
120 if (!ExpandEnvironmentStrings(input, address, addressLength))
121 {
122 delete[] address;
123 address = input;
124 }
125 }
126
127 CComPtr<IShellFolder> psfDesktop;
128 hr = SHGetDesktopFolder(&psfDesktop);
129 if (SUCCEEDED(hr))
130 {
131 hr = psfDesktop->ParseDisplayName(topLevelWindow, NULL, address, &eaten, &pidlLastParsed, &attributes);
132 }
133
134 if (address != input)
135 delete [] address;
136 delete [] input;
137
138 return hr;
139 }
140
141 HRESULT STDMETHODCALLTYPE CAddressEditBox::Execute(long paramC)
142 {
143 HRESULT hr;
144
145 /*
146 * Parse the path is it wasn't parsed
147 */
148 if (!pidlLastParsed)
149 ParseNow(0);
150
151 if (!pidlLastParsed)
152 return E_FAIL;
153
154 /*
155 * Get the IShellBrowser and IBrowserService interfaces of the shell browser
156 */
157 CComPtr<IShellBrowser> pisb;
158 hr = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &pisb));
159 if (FAILED(hr))
160 return hr;
161
162 CComPtr<IBrowserService> pbs;
163 pisb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs));
164 if (FAILED(hr))
165 return hr;
166
167 /*
168 * Get the current pidl of the shellbrowser and check if it is the same with the parsed one
169 */
170 PIDLIST_ABSOLUTE pidl;
171 hr = pbs->GetPidl(&pidl);
172 if (FAILED(hr))
173 return hr;
174
175 CComPtr<IShellFolder> psf;
176 hr = SHGetDesktopFolder(&psf);
177 if (FAILED(hr))
178 return hr;
179
180 hr = psf->CompareIDs(0, pidl, pidlLastParsed);
181
182 SHFree(pidl);
183 if (hr == 0)
184 return S_OK;
185
186 /*
187 * Attempt to browse to the parsed pidl
188 */
189 hr = pisb->BrowseObject(pidlLastParsed, 0);
190 if (SUCCEEDED(hr))
191 return hr;
192
193 /*
194 * Browsing to the pidl failed so it's not a folder. So invoke its defaule command.
195 */
196 HWND topLevelWindow;
197 hr = IUnknown_GetWindow(pisb, &topLevelWindow);
198 if (FAILED(hr))
199 return hr;
200
201 LPCITEMIDLIST pidlChild;
202 CComPtr<IShellFolder> sf;
203 hr = SHBindToParent(pidlLastParsed, IID_PPV_ARG(IShellFolder, &sf), &pidlChild);
204 if (FAILED(hr))
205 return hr;
206
207 hr = SHInvokeDefaultCommand(topLevelWindow, sf, pidlChild);
208 if (FAILED(hr))
209 return hr;
210
211 return hr;
212 }
213
214 HRESULT STDMETHODCALLTYPE CAddressEditBox::Save(long paramC)
215 {
216 return E_NOTIMPL;
217 }
218
219 HRESULT STDMETHODCALLTYPE CAddressEditBox::OnWinEvent(
220 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
221 {
222 LPNMHDR hdr;
223
224 *theResult = 0;
225
226 switch (uMsg)
227 {
228 case WM_NOTIFY:
229 hdr = (LPNMHDR) lParam;
230 if (hdr->code == CBEN_ENDEDIT)
231 {
232 NMCBEENDEDITW *endEdit = (NMCBEENDEDITW*) lParam;
233 if (endEdit->iWhy == CBENF_RETURN)
234 {
235 Execute(0);
236 }
237 else if (endEdit->iWhy == CBENF_ESCAPE)
238 {
239 /* Reset the contents of the combo box */
240 }
241 }
242 break;
243 }
244 return S_OK;
245 }
246
247 HRESULT STDMETHODCALLTYPE CAddressEditBox::IsWindowOwner(HWND hWnd)
248 {
249 if (fCombobox.m_hWnd == hWnd)
250 return S_OK;
251 if (fEditWindow.m_hWnd == hWnd)
252 return S_OK;
253 return S_FALSE;
254 }
255
256 HRESULT STDMETHODCALLTYPE CAddressEditBox::QueryStatus(
257 const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[ ], OLECMDTEXT *pCmdText)
258 {
259 return E_NOTIMPL;
260 }
261
262 HRESULT STDMETHODCALLTYPE CAddressEditBox::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
263 DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
264 {
265 return E_NOTIMPL;
266 }
267
268 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetTypeInfoCount(UINT *pctinfo)
269 {
270 return E_NOTIMPL;
271 }
272
273 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
274 {
275 return E_NOTIMPL;
276 }
277
278 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetIDsOfNames(
279 REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
280 {
281 return E_NOTIMPL;
282 }
283
284 HRESULT STDMETHODCALLTYPE CAddressEditBox::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
285 WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
286 {
287 CComPtr<IBrowserService> isb;
288 CComPtr<IShellFolder> sf;
289 HRESULT hr;
290 INT indexClosed, indexOpen, itemExists, oldIndex;
291 DWORD result;
292 COMBOBOXEXITEMW item;
293 PIDLIST_ABSOLUTE absolutePIDL;
294 LPCITEMIDLIST pidlChild;
295 LPITEMIDLIST pidlPrevious;
296 STRRET ret;
297 WCHAR buf[4096];
298
299 if (pDispParams == NULL)
300 return E_INVALIDARG;
301
302 switch (dispIdMember)
303 {
304 case DISPID_NAVIGATECOMPLETE2:
305 case DISPID_DOCUMENTCOMPLETE:
306 pidlLastParsed = NULL;
307
308 oldIndex = fCombobox.SendMessage(CB_GETCURSEL, 0, 0);
309
310 itemExists = FALSE;
311 pidlPrevious = NULL;
312
313 ZeroMemory(&item, sizeof(item));
314 item.mask = CBEIF_LPARAM;
315 item.iItem = 0;
316 if (fCombobox.SendMessage(CBEM_GETITEM, 0, reinterpret_cast<LPARAM>(&item)))
317 {
318 pidlPrevious = reinterpret_cast<LPITEMIDLIST>(item.lParam);
319 if (pidlPrevious)
320 itemExists = TRUE;
321 }
322
323 hr = IUnknown_QueryService(fSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &isb));
324 if (FAILED_UNEXPECTEDLY(hr))
325 return hr;
326 isb->GetPidl(&absolutePIDL);
327
328 SHBindToParent(absolutePIDL, IID_PPV_ARG(IShellFolder, &sf), &pidlChild);
329
330 sf->GetDisplayNameOf(pidlChild, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, &ret);
331
332 StrRetToBufW(&ret, pidlChild, buf, 4095);
333
334 indexClosed = SHMapPIDLToSystemImageListIndex(sf, pidlChild, &indexOpen);
335
336 item.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM;
337 item.iItem = 0;
338 item.iImage = indexClosed;
339 item.iSelectedImage = indexOpen;
340 item.pszText = buf;
341 item.lParam = reinterpret_cast<LPARAM>(absolutePIDL);
342
343 if (itemExists)
344 {
345 result = fCombobox.SendMessage(CBEM_SETITEM, 0, reinterpret_cast<LPARAM>(&item));
346 oldIndex = 0;
347
348 if (result)
349 {
350 ILFree(pidlPrevious);
351 }
352 }
353 else
354 {
355 oldIndex = fCombobox.SendMessage(CBEM_INSERTITEM, 0, reinterpret_cast<LPARAM>(&item));
356
357 if (oldIndex < 0)
358 DbgPrint("ERROR %d\n", GetLastError());
359 }
360
361 fCombobox.SendMessage(CB_SETCURSEL, -1, 0);
362 fCombobox.SendMessage(CB_SETCURSEL, oldIndex, 0);
363
364 //fAddressEditBox->SetCurrentDir(index);
365 }
366 return S_OK;
367 }
368
369 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetClassID(CLSID *pClassID)
370 {
371 if (pClassID == NULL)
372 return E_POINTER;
373 *pClassID = CLSID_AddressEditBox;
374 return S_OK;
375 }
376
377 HRESULT STDMETHODCALLTYPE CAddressEditBox::IsDirty()
378 {
379 return E_NOTIMPL;
380 }
381
382 HRESULT STDMETHODCALLTYPE CAddressEditBox::Load(IStream *pStm)
383 {
384 return E_NOTIMPL;
385 }
386
387 HRESULT STDMETHODCALLTYPE CAddressEditBox::Save(IStream *pStm, BOOL fClearDirty)
388 {
389 return E_NOTIMPL;
390 }
391
392 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetSizeMax(ULARGE_INTEGER *pcbSize)
393 {
394 return E_NOTIMPL;
395 }