xref: /reactos/dll/win32/browseui/addressband.cpp (revision 58aee30e)
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 Implements the navigation band of the cabinet window
23 */
24 
25 #include "precomp.h"
26 #include <commoncontrols.h>
27 #include <shlwapi_undoc.h>
28 #include <shellapi.h>
29 
30 /*
31 TODO:
32 ****Add tooltip notify handler
33   **Properly implement GetBandInfo
34     Implement Exec
35     Implement QueryService
36     Implement Load
37     Implement Save
38 */
39 
40 CAddressBand::CAddressBand()
41 {
42     fEditControl = NULL;
43     fGoButton = NULL;
44     fComboBox = NULL;
45     fGoButtonShown = false;
46 }
47 
48 CAddressBand::~CAddressBand()
49 {
50 }
51 
52 void CAddressBand::FocusChange(BOOL bFocus)
53 {
54 //    m_bFocus = bFocus;
55 
56     //Inform the input object site that the focus has changed.
57     if (fSite)
58     {
59 #if 0
60         fSite->OnFocusChangeIS((IDockingWindow *)this, bFocus);
61 #endif
62     }
63 }
64 
65 HRESULT STDMETHODCALLTYPE CAddressBand::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi)
66 {
67     if (!m_hWnd || !pdbi)  return E_FAIL;  /* Verify window exists */
68     if (pdbi->dwMask & DBIM_MINSIZE)
69     {
70         if (fGoButtonShown)
71             pdbi->ptMinSize.x = 100;
72         else
73             pdbi->ptMinSize.x = 150;
74         pdbi->ptMinSize.y = 22;
75     }
76     if (pdbi->dwMask & DBIM_MAXSIZE)
77     {
78         pdbi->ptMaxSize.x = 0;
79         pdbi->ptMaxSize.y = 0;
80     }
81     if (pdbi->dwMask & DBIM_INTEGRAL)
82     {
83         pdbi->ptIntegral.x = 0;
84         pdbi->ptIntegral.y = 0;
85     }
86     if (pdbi->dwMask & DBIM_ACTUAL)
87     {
88         if (fGoButtonShown)
89             pdbi->ptActual.x = 100;
90         else
91             pdbi->ptActual.x = 150;
92         pdbi->ptActual.y = 22;
93     }
94     if (pdbi->dwMask & DBIM_TITLE)
95     {
96         if (!LoadStringW(_AtlBaseModule.GetResourceInstance(), IDS_ADDRESSBANDLABEL, pdbi->wszTitle, _countof(pdbi->wszTitle)))
97             return HRESULT_FROM_WIN32(GetLastError());
98     }
99 
100     if (pdbi->dwMask & DBIM_MODEFLAGS)
101         pdbi->dwModeFlags = DBIMF_UNDELETEABLE;
102     if (pdbi->dwMask & DBIM_BKCOLOR)
103         pdbi->crBkgnd = 0;
104     return S_OK;
105 }
106 
107 HRESULT STDMETHODCALLTYPE CAddressBand::SetSite(IUnknown *pUnkSite)
108 {
109     CComPtr<IShellService>                  shellService;
110     HWND                                    parentWindow;
111     HWND                                    combobox;
112     HRESULT                                 hResult;
113     IImageList                              *piml;
114 
115     if (pUnkSite == NULL)
116     {
117         fSite.Release();
118         return S_OK;
119     }
120 
121     fSite.Release();
122 
123     hResult = pUnkSite->QueryInterface(IID_PPV_ARG(IDockingWindowSite, &fSite));
124     if (FAILED_UNEXPECTEDLY(hResult))
125         return hResult;
126 
127     // get window handle of parent
128     parentWindow = NULL;
129     hResult = IUnknown_GetWindow(fSite, &parentWindow);
130 
131     if (!::IsWindow(parentWindow))
132         return E_FAIL;
133 
134     // create combo box ex
135     combobox = CreateWindowEx(WS_EX_TOOLWINDOW, WC_COMBOBOXEXW, NULL, WS_CHILD | WS_VISIBLE |
136         WS_CLIPCHILDREN | WS_TABSTOP | CCS_NODIVIDER | CCS_NOMOVEY | CBS_OWNERDRAWFIXED,
137                     0, 0, 500, 250, parentWindow, (HMENU)IDM_TOOLBARS_ADDRESSBAR, _AtlBaseModule.GetModuleInstance(), 0);
138     if (combobox == NULL)
139         return E_FAIL;
140     SubclassWindow(combobox);
141 
142     HRESULT hr = SHGetImageList(SHIL_SMALL, IID_PPV_ARG(IImageList, &piml));
143     if (FAILED_UNEXPECTEDLY(hr))
144     {
145         SendMessageW(combobox, CBEM_SETIMAGELIST, 0, 0);
146     }
147     else
148     {
149         SendMessageW(combobox, CBEM_SETIMAGELIST, 0, reinterpret_cast<LPARAM>(piml));
150     }
151 
152     SendMessage(CBEM_SETEXTENDEDSTYLE,
153         CBES_EX_CASESENSITIVE | CBES_EX_NOSIZELIMIT, CBES_EX_CASESENSITIVE | CBES_EX_NOSIZELIMIT);
154 
155     fEditControl = reinterpret_cast<HWND>(SendMessage(CBEM_GETEDITCONTROL, 0, 0));
156     fComboBox = reinterpret_cast<HWND>(SendMessage(CBEM_GETCOMBOCONTROL, 0, 0));
157     hResult = CAddressEditBox_CreateInstance(IID_PPV_ARG(IAddressEditBox, &fAddressEditBox));
158     if (FAILED_UNEXPECTEDLY(hResult))
159         return hResult;
160 
161     hResult = fAddressEditBox->QueryInterface(IID_PPV_ARG(IShellService, &shellService));
162     if (FAILED_UNEXPECTEDLY(hResult))
163         return hResult;
164     hResult = fAddressEditBox->Init(combobox, fEditControl, 8, fSite /*(IAddressBand *)this*/);
165     if (FAILED_UNEXPECTEDLY(hResult))
166         return hResult;
167     hResult = shellService->SetOwner(fSite);
168     if (FAILED_UNEXPECTEDLY(hResult))
169         return hResult;
170 
171     fGoButtonShown = SHRegGetBoolUSValueW(L"Software\\Microsoft\\Internet Explorer\\Main", L"ShowGoButton", FALSE, TRUE);
172     if (fGoButtonShown)
173         CreateGoButton();
174 
175     return hResult;
176 }
177 
178 HRESULT STDMETHODCALLTYPE CAddressBand::GetSite(REFIID riid, void **ppvSite)
179 {
180     if (fSite == NULL)
181         return E_FAIL;
182     return fSite->QueryInterface(riid, ppvSite);
183 }
184 
185 HRESULT STDMETHODCALLTYPE CAddressBand::GetWindow(HWND *lphwnd)
186 {
187     if (lphwnd == NULL)
188         return E_POINTER;
189     *lphwnd = m_hWnd;
190     return S_OK;
191 }
192 
193 HRESULT STDMETHODCALLTYPE CAddressBand::ContextSensitiveHelp(BOOL fEnterMode)
194 {
195     return E_NOTIMPL;
196 }
197 
198 HRESULT STDMETHODCALLTYPE CAddressBand::CloseDW(DWORD dwReserved)
199 {
200     ShowDW(FALSE);
201 
202     if (IsWindow())
203         DestroyWindow();
204 
205     m_hWnd = NULL;
206 
207     CComPtr<IShellService> pservice;
208     HRESULT hres = fAddressEditBox->QueryInterface(IID_PPV_ARG(IShellService, &pservice));
209     if (SUCCEEDED(hres ))
210         pservice->SetOwner(NULL);
211 
212     if (fAddressEditBox) fAddressEditBox.Release();
213     if (fSite) fSite.Release();
214 
215     if (m_himlNormal)
216         ImageList_Destroy(m_himlNormal);
217 
218     if (m_himlHot)
219         ImageList_Destroy(m_himlHot);
220 
221     return S_OK;
222 }
223 
224 HRESULT STDMETHODCALLTYPE CAddressBand::ResizeBorderDW(
225     const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
226 {
227     return E_NOTIMPL;
228 }
229 
230 HRESULT STDMETHODCALLTYPE CAddressBand::ShowDW(BOOL fShow)
231 {
232     if (m_hWnd)
233     {
234         if (fShow)
235             ShowWindow(SW_SHOW);
236         else
237             ShowWindow(SW_HIDE);
238     }
239     return S_OK;
240 }
241 
242 HRESULT STDMETHODCALLTYPE CAddressBand::QueryStatus(
243     const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[  ], OLECMDTEXT *pCmdText)
244 {
245     return IUnknown_QueryStatus(fAddressEditBox, *pguidCmdGroup, cCmds, prgCmds, pCmdText);
246 }
247 
248 HRESULT STDMETHODCALLTYPE CAddressBand::Exec(const GUID *pguidCmdGroup,
249     DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
250 {
251     // incomplete
252     return E_NOTIMPL;
253 }
254 
255 HRESULT STDMETHODCALLTYPE CAddressBand::HasFocusIO()
256 {
257     if (GetFocus() == fEditControl || SendMessage(CB_GETDROPPEDSTATE, 0, 0))
258         return S_OK;
259     return S_FALSE;
260 }
261 
262 HRESULT STDMETHODCALLTYPE CAddressBand::TranslateAcceleratorIO(LPMSG lpMsg)
263 {
264     if (lpMsg->hwnd == fEditControl)
265     {
266         switch (lpMsg->message)
267         {
268         case WM_SYSKEYDOWN:
269         case WM_SYSKEYUP:
270         case WM_SYSCOMMAND:
271         case WM_SYSDEADCHAR:
272         case WM_SYSCHAR:
273             return S_FALSE;
274         }
275 
276         TranslateMessage(lpMsg);
277         DispatchMessage(lpMsg);
278         return S_OK;
279     }
280     return S_FALSE;
281 }
282 
283 HRESULT STDMETHODCALLTYPE CAddressBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
284 {
285     if (fActivate)
286     {
287         IUnknown_OnFocusChangeIS(fSite, static_cast<IDeskBand *>(this), fActivate);
288         SetFocus();
289     }
290     return S_OK;
291 }
292 
293 HRESULT STDMETHODCALLTYPE CAddressBand::OnWinEvent(
294     HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
295 {
296     CComPtr<IWinEventHandler>               winEventHandler;
297     HRESULT                                 hResult;
298     RECT                                    rect;
299 
300     if (theResult)
301         *theResult = 0;
302 
303     switch (uMsg)
304     {
305         case WM_WININICHANGE:
306             break;
307         case WM_COMMAND:
308             if (wParam == IDM_TOOLBARS_GOBUTTON)
309             {
310                 fGoButtonShown = !SHRegGetBoolUSValueW(L"Software\\Microsoft\\Internet Explorer\\Main", L"ShowGoButton", FALSE, TRUE);
311                 SHRegSetUSValueW(L"Software\\Microsoft\\Internet Explorer\\Main", L"ShowGoButton", REG_SZ, fGoButtonShown ? (LPVOID)L"yes" : (LPVOID)L"no", fGoButtonShown ? 8 : 6, SHREGSET_FORCE_HKCU);
312                 if (!fGoButton)
313                     CreateGoButton();
314                 ::ShowWindow(fGoButton,fGoButtonShown ? SW_HIDE : SW_SHOW);
315                 GetWindowRect(&rect);
316                 SendMessage(m_hWnd,WM_SIZE,0,MAKELPARAM(rect.right-rect.left,rect.bottom-rect.top));
317                 // broadcast change notification to all explorer windows
318             }
319             break;
320     }
321     hResult = fAddressEditBox->QueryInterface(IID_PPV_ARG(IWinEventHandler, &winEventHandler));
322     if (FAILED_UNEXPECTEDLY(hResult))
323         return hResult;
324     return winEventHandler->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult);
325 }
326 
327 HRESULT STDMETHODCALLTYPE CAddressBand::IsWindowOwner(HWND hWnd)
328 {
329     CComPtr<IWinEventHandler>               winEventHandler;
330     HRESULT                                 hResult;
331 
332     if (fAddressEditBox)
333     {
334         hResult = fAddressEditBox->QueryInterface(IID_PPV_ARG(IWinEventHandler, &winEventHandler));
335         if (FAILED_UNEXPECTEDLY(hResult))
336             return hResult;
337         return winEventHandler->IsWindowOwner(hWnd);
338     }
339     return S_FALSE;
340 }
341 
342 HRESULT STDMETHODCALLTYPE CAddressBand::FileSysChange(long param8, long paramC)
343 {
344     CComPtr<IAddressBand>                   addressBand;
345     HRESULT                                 hResult;
346 
347     hResult = fAddressEditBox->QueryInterface(IID_PPV_ARG(IAddressBand, &addressBand));
348     if (FAILED_UNEXPECTEDLY(hResult))
349         return hResult;
350     return addressBand->FileSysChange(param8, paramC);
351 }
352 
353 HRESULT STDMETHODCALLTYPE CAddressBand::Refresh(long param8)
354 {
355     CComPtr<IAddressBand>                   addressBand;
356     HRESULT                                 hResult;
357 
358     hResult = fAddressEditBox->QueryInterface(IID_PPV_ARG(IAddressBand, &addressBand));
359     if (FAILED_UNEXPECTEDLY(hResult))
360         return hResult;
361     return addressBand->Refresh(param8);
362 }
363 
364 HRESULT STDMETHODCALLTYPE CAddressBand::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
365 {
366     return E_NOTIMPL;
367 }
368 
369 HRESULT STDMETHODCALLTYPE CAddressBand::OnFocusChangeIS(IUnknown *punkObj, BOOL fSetFocus)
370 {
371     return E_NOTIMPL;
372 }
373 
374 HRESULT STDMETHODCALLTYPE CAddressBand::GetClassID(CLSID *pClassID)
375 {
376     if (pClassID == NULL)
377         return E_POINTER;
378     *pClassID = CLSID_SH_AddressBand;
379     return S_OK;
380 }
381 
382 HRESULT STDMETHODCALLTYPE CAddressBand::IsDirty()
383 {
384     return E_NOTIMPL;
385 }
386 
387 HRESULT STDMETHODCALLTYPE CAddressBand::Load(IStream *pStm)
388 {
389     // incomplete
390     return E_NOTIMPL;
391 }
392 
393 HRESULT STDMETHODCALLTYPE CAddressBand::Save(IStream *pStm, BOOL fClearDirty)
394 {
395     // incomplete
396     return E_NOTIMPL;
397 }
398 
399 HRESULT STDMETHODCALLTYPE CAddressBand::GetSizeMax(ULARGE_INTEGER *pcbSize)
400 {
401     // incomplete
402     return E_NOTIMPL;
403 }
404 
405 LRESULT CAddressBand::OnNotifyClick(WPARAM wParam, NMHDR *notifyHeader, BOOL &bHandled)
406 {
407     if (notifyHeader->hwndFrom == fGoButton)
408     {
409         fAddressEditBox->Execute(0);
410     }
411     return 0;
412 }
413 
414 LRESULT CAddressBand::OnTipText(UINT idControl, NMHDR *notifyHeader, BOOL &bHandled)
415 {
416     if (notifyHeader->hwndFrom == fGoButton)
417     {
418         WCHAR szText[MAX_PATH];
419         WCHAR szFormat[MAX_PATH];
420         LPNMTBGETINFOTIP pGIT = (LPNMTBGETINFOTIP)notifyHeader;
421 
422         if (::GetWindowTextW(fEditControl, szText, _countof(szText)))
423         {
424             LoadStringW(_AtlBaseModule.GetResourceInstance(), IDS_GOBUTTONTIPTEMPLATE, szFormat, _countof(szFormat));
425             wnsprintf(pGIT->pszText, pGIT->cchTextMax, szFormat, szText);
426         }
427         else
428             *pGIT->pszText = 0;
429     }
430     return 0;
431 }
432 
433 LRESULT CAddressBand::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
434 {
435     POINT                                   pt;
436     POINT                                   ptOrig;
437     HWND                                    parentWindow;
438     LRESULT                                 result;
439 
440     if (fGoButtonShown == false)
441     {
442         bHandled = FALSE;
443         return 0;
444     }
445     pt.x = 0;
446     pt.y = 0;
447     parentWindow = GetParent();
448     ::MapWindowPoints(m_hWnd, parentWindow, &pt, 1);
449     OffsetWindowOrgEx(reinterpret_cast<HDC>(wParam), pt.x, pt.y, &ptOrig);
450     result = SendMessage(parentWindow, WM_ERASEBKGND, wParam, 0);
451     SetWindowOrgEx(reinterpret_cast<HDC>(wParam), ptOrig.x, ptOrig.y, NULL);
452     if (result == 0)
453     {
454         bHandled = FALSE;
455         return 0;
456     }
457     return result;
458 }
459 
460 LRESULT CAddressBand::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
461 {
462     RECT                                    goButtonBounds;
463     RECT                                    buttonBounds;
464     long                                    buttonWidth;
465     long                                    buttonHeight;
466     RECT                                    comboBoxBounds;
467     long                                    newHeight;
468     long                                    newWidth;
469 
470     if (fGoButtonShown == false)
471     {
472         bHandled = FALSE;
473         return 0;
474     }
475 
476     newHeight = HIWORD(lParam);
477     newWidth = LOWORD(lParam);
478 
479     if (!fGoButton)
480         CreateGoButton();
481 
482     SendMessage(fGoButton, TB_GETITEMRECT, 0, reinterpret_cast<LPARAM>(&buttonBounds));
483     buttonWidth = buttonBounds.right - buttonBounds.left;
484     buttonHeight = buttonBounds.bottom - buttonBounds.top;
485 
486     DefWindowProc(WM_SIZE, wParam, MAKELONG(newWidth - buttonWidth - 2, newHeight));
487     ::GetWindowRect(fComboBox, &comboBoxBounds);
488     ::SetWindowPos(fGoButton, NULL, newWidth - buttonWidth, (comboBoxBounds.bottom - comboBoxBounds.top - buttonHeight) / 2,
489                     buttonWidth, buttonHeight, SWP_NOOWNERZORDER | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOZORDER);
490 
491     goButtonBounds.left = newWidth - buttonWidth;
492     goButtonBounds.top = 0;
493     goButtonBounds.right = newWidth - buttonWidth;
494     goButtonBounds.bottom = newHeight;
495     InvalidateRect(&goButtonBounds, TRUE);
496 
497     SendMessage(fComboBox, CB_SETDROPPEDWIDTH, 200, 0);
498     return 0;
499 }
500 
501 LRESULT CAddressBand::OnWindowPosChanging(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
502 {
503     RECT                                    goButtonBounds;
504     RECT                                    buttonBounds;
505     long                                    buttonWidth;
506     long                                    buttonHeight;
507     RECT                                    comboBoxBounds;
508     WINDOWPOS                               positionInfoCopy;
509     long                                    newHeight;
510     long                                    newWidth;
511 
512     if (!fGoButtonShown)
513     {
514         bHandled = FALSE;
515         return 0;
516     }
517 
518     if (!fGoButton)
519         CreateGoButton();
520 
521     positionInfoCopy = *reinterpret_cast<WINDOWPOS *>(lParam);
522     newHeight = positionInfoCopy.cy;
523     newWidth = positionInfoCopy.cx;
524 
525     SendMessage(fGoButton, TB_GETITEMRECT, 0, reinterpret_cast<LPARAM>(&buttonBounds));
526 
527     buttonWidth = buttonBounds.right - buttonBounds.left;
528     buttonHeight = buttonBounds.bottom - buttonBounds.top;
529     positionInfoCopy.cx = newWidth - 2 - buttonWidth;
530     DefWindowProc(WM_WINDOWPOSCHANGING, wParam, reinterpret_cast<LPARAM>(&positionInfoCopy));
531     ::GetWindowRect(fComboBox, &comboBoxBounds);
532     ::SetWindowPos(fGoButton, NULL, newWidth - buttonWidth, (comboBoxBounds.bottom - comboBoxBounds.top - buttonHeight) / 2,
533                     buttonWidth, buttonHeight, SWP_NOOWNERZORDER | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOZORDER);
534 
535     goButtonBounds.left = newWidth - buttonWidth;
536     goButtonBounds.top = 0;
537     goButtonBounds.right = newWidth - buttonWidth;
538     goButtonBounds.bottom = newHeight;
539     InvalidateRect(&goButtonBounds, TRUE);
540 
541     SendMessage(fComboBox, CB_SETDROPPEDWIDTH, 200, 0);
542     return 0;
543 }
544 
545 void CAddressBand::CreateGoButton()
546 {
547     const TBBUTTON buttonInfo [] = { { 0, 1, TBSTATE_ENABLED, 0 } };
548     HINSTANCE             shellInstance;
549 
550     shellInstance = _AtlBaseModule.GetResourceInstance();
551     m_himlNormal = ImageList_LoadImageW(shellInstance, MAKEINTRESOURCE(IDB_GOBUTTON_NORMAL),
552                                            20, 0, RGB(255, 0, 255), IMAGE_BITMAP, LR_CREATEDIBSECTION);
553     m_himlHot = ImageList_LoadImageW(shellInstance, MAKEINTRESOURCE(IDB_GOBUTTON_HOT),
554                                         20, 0, RGB(255, 0, 255), IMAGE_BITMAP, LR_CREATEDIBSECTION);
555 
556     fGoButton = CreateWindowEx(WS_EX_TOOLWINDOW, TOOLBARCLASSNAMEW, 0, WS_CHILD | WS_CLIPSIBLINGS |
557                                WS_CLIPCHILDREN | TBSTYLE_LIST | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | CCS_NODIVIDER |
558                                CCS_NOPARENTALIGN | CCS_NORESIZE,
559                                0, 0, 0, 0, m_hWnd, NULL, _AtlBaseModule.GetModuleInstance(), NULL);
560     SendMessage(fGoButton, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
561     SendMessage(fGoButton, TB_SETMAXTEXTROWS, 1, 0);
562     if (m_himlNormal)
563         SendMessage(fGoButton, TB_SETIMAGELIST, 0, reinterpret_cast<LPARAM>(m_himlNormal));
564     if (m_himlHot)
565         SendMessage(fGoButton, TB_SETHOTIMAGELIST, 0, reinterpret_cast<LPARAM>(m_himlHot));
566     SendMessage(fGoButton, TB_ADDSTRINGW,
567                 reinterpret_cast<WPARAM>(_AtlBaseModule.GetResourceInstance()), IDS_GOBUTTONLABEL);
568     SendMessage(fGoButton, TB_ADDBUTTONSW, 1, (LPARAM) &buttonInfo);
569 }
570