shdocvw: Add support for setting the statusbar text in IE.
[wine] / dlls / shdocvw / iexplore.c
1 /*
2  * SHDOCVW - Internet Explorer main frame window
3  *
4  * Copyright 2006 Mike McCormack (for CodeWeavers)
5  * Copyright 2006 Jacek Caban (for CodeWeavers)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define COBJMACROS
23
24 #include <stdarg.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "wingdi.h"
29 #include "winnls.h"
30 #include "ole2.h"
31 #include "exdisp.h"
32 #include "oleidl.h"
33
34 #include "shdocvw.h"
35 #include "mshtmcid.h"
36 #include "shellapi.h"
37
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(shdocvw);
41
42 #define IDI_APPICON 1
43
44 #define DOCHOST_THIS(iface) DEFINE_THIS2(InternetExplorer,doc_host,iface)
45
46 static const WCHAR szIEWinFrame[] = { 'I','E','F','r','a','m','e',0 };
47
48 /* Windows uses "Microsoft Internet Explorer" */
49 static const WCHAR wszWineInternetExplorer[] =
50         {'W','i','n','e',' ','I','n','t','e','r','n','e','t',' ','E','x','p','l','o','r','e','r',0};
51
52 HRESULT update_ie_statustext(InternetExplorer* This, LPCWSTR text)
53 {
54     if(!SendMessageW(This->status_hwnd, SB_SETTEXTW, MAKEWORD(SB_SIMPLEID, 0), (LPARAM)text))
55         return E_FAIL;
56
57     return S_OK;
58 }
59
60 void adjust_ie_docobj_rect(HWND frame, RECT* rc)
61 {
62     HWND hwndRebar = GetDlgItem(frame, IDC_BROWSE_REBAR);
63     HWND hwndStatus = GetDlgItem(frame, IDC_BROWSE_STATUSBAR);
64     INT barHeight = SendMessageW(hwndRebar, RB_GETBARHEIGHT, 0, 0);
65
66     rc->top += barHeight;
67     rc->bottom -= barHeight;
68
69     if(IsWindowVisible(hwndStatus))
70     {
71         RECT statusrc;
72
73         GetClientRect(hwndStatus, &statusrc);
74         rc->bottom -= statusrc.bottom - statusrc.top;
75     }
76 }
77
78 static INT_PTR CALLBACK ie_dialog_open_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
79 {
80     static InternetExplorer* This;
81
82     switch(msg)
83     {
84         case WM_INITDIALOG:
85             This = (InternetExplorer*)lparam;
86             EnableWindow(GetDlgItem(hwnd, IDOK), FALSE);
87             return TRUE;
88
89         case WM_COMMAND:
90             switch(LOWORD(wparam))
91             {
92                 case IDC_BROWSE_OPEN_URL:
93                 {
94                     HWND hwndurl = GetDlgItem(hwnd, IDC_BROWSE_OPEN_URL);
95                     int len = GetWindowTextLengthW(hwndurl);
96
97                     EnableWindow(GetDlgItem(hwnd, IDOK), len ? TRUE : FALSE);
98                     break;
99                 }
100                 case IDOK:
101                 {
102                     HWND hwndurl = GetDlgItem(hwnd, IDC_BROWSE_OPEN_URL);
103                     int len = GetWindowTextLengthW(hwndurl);
104
105                     if(len)
106                     {
107                         VARIANT url;
108
109                         V_VT(&url) = VT_BSTR;
110                         V_BSTR(&url) = SysAllocStringLen(NULL, len);
111
112                         GetWindowTextW(hwndurl, V_BSTR(&url), len);
113                         IWebBrowser2_Navigate2(WEBBROWSER2(This), &url, NULL, NULL, NULL, NULL);
114
115                         SysFreeString(V_BSTR(&url));
116                     }
117                 }
118                 /* fall through */
119                 case IDCANCEL:
120                     EndDialog(hwnd, wparam);
121                     return TRUE;
122             }
123     }
124     return FALSE;
125 }
126
127 static void ie_dialog_about(HWND hwnd)
128 {
129     HICON icon = LoadImageW(GetModuleHandleW(0), MAKEINTRESOURCEW(IDI_APPICON), IMAGE_ICON, 48, 48, LR_SHARED);
130
131     ShellAboutW(hwnd, wszWineInternetExplorer, NULL, icon);
132
133     DestroyIcon(icon);
134 }
135
136 static void create_rebar(HWND hwnd)
137 {
138     HWND hwndRebar;
139     HWND hwndAddress;
140     REBARINFO rebarinf;
141     REBARBANDINFOW bandinf;
142     WCHAR addr[] = {'A','d','d','r','e','s','s',0};
143
144     hwndRebar = CreateWindowExW(WS_EX_TOOLWINDOW, REBARCLASSNAMEW, NULL, WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|RBS_VARHEIGHT|CCS_TOP|CCS_NODIVIDER, 0, 0, 0, 0, hwnd, (HMENU)IDC_BROWSE_REBAR, shdocvw_hinstance, NULL);
145
146     rebarinf.cbSize = sizeof(rebarinf);
147     rebarinf.fMask = 0;
148     rebarinf.himl = NULL;
149     rebarinf.cbSize = sizeof(rebarinf);
150
151     SendMessageW(hwndRebar, RB_SETBARINFO, 0, (LPARAM)&rebarinf);
152
153     hwndAddress = CreateWindowExW(0, WC_COMBOBOXEXW, NULL, WS_BORDER|WS_CHILD|WS_VISIBLE|CBS_DROPDOWN, 0, 0, 100,20,hwndRebar, (HMENU)IDC_BROWSE_ADDRESSBAR, shdocvw_hinstance, NULL);
154
155     bandinf.fMask = RBBIM_STYLE | RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_SIZE | RBBIM_TEXT;
156     bandinf.fStyle = RBBS_CHILDEDGE | RBBS_GRIPPERALWAYS;
157     bandinf.lpText = addr;
158     bandinf.cx = 100;
159     bandinf.cyMinChild = 20;
160     bandinf.hwndChild = hwndAddress;
161
162     SendMessageW(hwndRebar, RB_INSERTBANDW, 0, (LPARAM)&bandinf);
163 }
164
165 static LRESULT iewnd_OnCreate(HWND hwnd, LPCREATESTRUCTW lpcs)
166 {
167     InternetExplorer* This = (InternetExplorer*)lpcs->lpCreateParams;
168     SetWindowLongPtrW(hwnd, 0, (LONG_PTR) lpcs->lpCreateParams);
169
170     This->status_hwnd = CreateStatusWindowW(CCS_NODIVIDER|WS_CHILD|WS_VISIBLE, NULL, hwnd, IDC_BROWSE_STATUSBAR);
171     SendMessageW(This->status_hwnd, SB_SIMPLE, TRUE, 0);
172
173     create_rebar(hwnd);
174
175     return 0;
176 }
177
178 static LRESULT iewnd_OnSize(InternetExplorer *This, INT width, INT height)
179 {
180     HWND hwndRebar = GetDlgItem(This->frame_hwnd, IDC_BROWSE_REBAR);
181     INT barHeight = SendMessageW(hwndRebar, RB_GETBARHEIGHT, 0, 0);
182     RECT docarea = {0, 0, width, height};
183
184     SendMessageW(This->status_hwnd, WM_SIZE, 0, 0);
185
186     adjust_ie_docobj_rect(This->frame_hwnd, &docarea);
187
188     if(This->doc_host.hwnd)
189         SetWindowPos(This->doc_host.hwnd, NULL, docarea.left, docarea.top, docarea.right, docarea.bottom,
190                      SWP_NOZORDER | SWP_NOACTIVATE);
191
192     SetWindowPos(hwndRebar, NULL, 0, 0, width, barHeight, SWP_NOZORDER | SWP_NOACTIVATE);
193
194     return 0;
195 }
196
197 static LRESULT iewnd_OnNotify(InternetExplorer *This, WPARAM wparam, LPARAM lparam)
198 {
199     NMHDR* hdr = (NMHDR*)lparam;
200
201     if(hdr->idFrom == IDC_BROWSE_ADDRESSBAR && hdr->code == CBEN_ENDEDITW)
202     {
203         NMCBEENDEDITW* info = (NMCBEENDEDITW*)lparam;
204
205         if(info->fChanged && info->iWhy == CBENF_RETURN && info->szText)
206         {
207             VARIANT vt;
208
209             V_VT(&vt) = VT_BSTR;
210             V_BSTR(&vt) = SysAllocString(info->szText);
211
212             IWebBrowser2_Navigate2(WEBBROWSER2(This), &vt, NULL, NULL, NULL, NULL);
213
214             SysFreeString(V_BSTR(&vt));
215
216             return 0;
217         }
218     }
219
220     return 0;
221 }
222
223 static LRESULT iewnd_OnDestroy(InternetExplorer *This)
224 {
225     TRACE("%p\n", This);
226
227     This->frame_hwnd = NULL;
228     PostQuitMessage(0); /* FIXME */
229
230     return 0;
231 }
232
233 static LRESULT CALLBACK iewnd_OnCommand(InternetExplorer *This, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
234 {
235     switch(LOWORD(wparam))
236     {
237         case ID_BROWSE_OPEN:
238             DialogBoxParamW(shdocvw_hinstance, MAKEINTRESOURCEW(IDD_BROWSE_OPEN), hwnd, ie_dialog_open_proc, (LPARAM)This);
239             break;
240
241         case ID_BROWSE_PRINT:
242             if(This->doc_host.document)
243             {
244                 IOleCommandTarget* target;
245
246                 if(FAILED(IUnknown_QueryInterface(This->doc_host.document, &IID_IOleCommandTarget, (LPVOID*)&target)))
247                     break;
248
249                 IOleCommandTarget_Exec(target, &CGID_MSHTML, IDM_PRINT, OLECMDEXECOPT_DODEFAULT, NULL, NULL);
250
251                 IOleCommandTarget_Release(target);
252             }
253             break;
254
255         case ID_BROWSE_ABOUT:
256             ie_dialog_about(hwnd);
257             break;
258
259         default:
260             return DefWindowProcW(hwnd, msg, wparam, lparam);
261     }
262     return 0;
263 }
264
265 static LRESULT CALLBACK update_addrbar(InternetExplorer *This, LPARAM lparam)
266 {
267     HWND hwndRebar = GetDlgItem(This->frame_hwnd, IDC_BROWSE_REBAR);
268     HWND hwndAddress = GetDlgItem(hwndRebar, IDC_BROWSE_ADDRESSBAR);
269     HWND hwndEdit = (HWND)SendMessageW(hwndAddress, CBEM_GETEDITCONTROL, 0, 0);
270     LPCWSTR url = (LPCWSTR)lparam;
271
272     SendMessageW(hwndEdit, WM_SETTEXT, 0, (LPARAM)url);
273
274     return 0;
275 }
276
277 static LRESULT CALLBACK
278 ie_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
279 {
280     InternetExplorer *This = (InternetExplorer*) GetWindowLongPtrW(hwnd, 0);
281
282     switch (msg)
283     {
284     case WM_CREATE:
285         return iewnd_OnCreate(hwnd, (LPCREATESTRUCTW)lparam);
286     case WM_DESTROY:
287         return iewnd_OnDestroy(This);
288     case WM_SIZE:
289         return iewnd_OnSize(This, LOWORD(lparam), HIWORD(lparam));
290     case WM_COMMAND:
291         return iewnd_OnCommand(This, hwnd, msg, wparam, lparam);
292     case WM_NOTIFY:
293         return iewnd_OnNotify(This, wparam, lparam);
294     case WM_DOCHOSTTASK:
295         return process_dochost_task(&This->doc_host, lparam);
296     case WM_UPDATEADDRBAR:
297         return update_addrbar(This, lparam);
298     }
299     return DefWindowProcW(hwnd, msg, wparam, lparam);
300 }
301
302 void register_iewindow_class(void)
303 {
304     WNDCLASSEXW wc;
305
306     memset(&wc, 0, sizeof wc);
307     wc.cbSize = sizeof(wc);
308     wc.style = 0;
309     wc.lpfnWndProc = ie_window_proc;
310     wc.cbClsExtra = 0;
311     wc.cbWndExtra = sizeof(InternetExplorer*);
312     wc.hInstance = shdocvw_hinstance;
313     wc.hIcon = LoadIconW(GetModuleHandleW(0), MAKEINTRESOURCEW(IDI_APPICON));
314     wc.hIconSm = LoadImageW(GetModuleHandleW(0), MAKEINTRESOURCEW(IDI_APPICON), IMAGE_ICON,
315                             GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
316     wc.hCursor = LoadCursorW(0, MAKEINTRESOURCEW(IDC_ARROW));
317     wc.hbrBackground = 0;
318     wc.lpszClassName = szIEWinFrame;
319     wc.lpszMenuName = NULL;
320
321     RegisterClassExW(&wc);
322 }
323
324 void unregister_iewindow_class(void)
325 {
326     UnregisterClassW(szIEWinFrame, shdocvw_hinstance);
327 }
328
329 static void create_frame_hwnd(InternetExplorer *This)
330 {
331     This->frame_hwnd = CreateWindowExW(
332             WS_EX_WINDOWEDGE,
333             szIEWinFrame, wszWineInternetExplorer,
334             WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
335                 | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
336             CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
337             NULL, NULL /* FIXME */, shdocvw_hinstance, This);
338 }
339
340 static IWebBrowser2 *create_ie_window(LPCSTR cmdline)
341 {
342     IWebBrowser2 *wb = NULL;
343
344     InternetExplorer_Create(NULL, &IID_IWebBrowser2, (void**)&wb);
345     if(!wb)
346         return NULL;
347
348     IWebBrowser2_put_Visible(wb, VARIANT_TRUE);
349     IWebBrowser2_put_MenuBar(wb, VARIANT_TRUE);
350
351     if(!*cmdline) {
352         IWebBrowser2_GoHome(wb);
353     }else {
354         VARIANT var_url;
355         DWORD len;
356         int cmdlen;
357
358         if(!strncasecmp(cmdline, "-nohome", 7))
359             cmdline += 7;
360         while(*cmdline == ' ' || *cmdline == '\t')
361             cmdline++;
362         cmdlen = lstrlenA(cmdline);
363         if(cmdlen > 2 && cmdline[0] == '"' && cmdline[cmdlen-1] == '"') {
364             cmdline++;
365             cmdlen -= 2;
366         }
367
368         V_VT(&var_url) = VT_BSTR;
369
370         len = MultiByteToWideChar(CP_ACP, 0, cmdline, cmdlen, NULL, 0);
371         V_BSTR(&var_url) = SysAllocStringLen(NULL, len);
372         MultiByteToWideChar(CP_ACP, 0, cmdline, cmdlen, V_BSTR(&var_url), len);
373
374         /* navigate to the first page */
375         IWebBrowser2_Navigate2(wb, &var_url, NULL, NULL, NULL, NULL);
376
377         SysFreeString(V_BSTR(&var_url));
378     }
379
380     return wb;
381 }
382
383 static void WINAPI DocHostContainer_GetDocObjRect(DocHost* This, RECT* rc)
384 {
385     GetClientRect(This->frame_hwnd, rc);
386     adjust_ie_docobj_rect(This->frame_hwnd, rc);
387 }
388
389 static HRESULT WINAPI DocHostContainer_SetStatusText(DocHost* This, LPCWSTR text)
390 {
391     InternetExplorer* ie = DOCHOST_THIS(This);
392     return update_ie_statustext(ie, text);
393 }
394
395 static void WINAPI DocHostContainer_SetURL(DocHost* This, LPCWSTR url)
396 {
397     SendMessageW(This->frame_hwnd, WM_UPDATEADDRBAR, 0, (LPARAM)url);
398 }
399
400 static const IDocHostContainerVtbl DocHostContainerVtbl = {
401     DocHostContainer_GetDocObjRect,
402     DocHostContainer_SetStatusText,
403     DocHostContainer_SetURL
404 };
405
406 HRESULT InternetExplorer_Create(IUnknown *pOuter, REFIID riid, void **ppv)
407 {
408     InternetExplorer *ret;
409     HRESULT hres;
410
411     TRACE("(%p %s %p)\n", pOuter, debugstr_guid(riid), ppv);
412
413     ret = heap_alloc_zero(sizeof(InternetExplorer));
414     ret->ref = 0;
415
416     ret->doc_host.disp = (IDispatch*)WEBBROWSER2(ret);
417     DocHost_Init(&ret->doc_host, (IDispatch*)WEBBROWSER2(ret), &DocHostContainerVtbl);
418
419     InternetExplorer_WebBrowser_Init(ret);
420
421     HlinkFrame_Init(&ret->hlink_frame, (IUnknown*)WEBBROWSER2(ret), &ret->doc_host);
422
423     create_frame_hwnd(ret);
424     ret->doc_host.frame_hwnd = ret->frame_hwnd;
425
426     hres = IWebBrowser2_QueryInterface(WEBBROWSER2(ret), riid, ppv);
427     if(FAILED(hres)) {
428         heap_free(ret);
429         return hres;
430     }
431
432     return hres;
433 }
434
435 /******************************************************************
436  *              IEWinMain            (SHDOCVW.101)
437  *
438  * Only returns on error.
439  */
440 DWORD WINAPI IEWinMain(LPSTR szCommandLine, int nShowWindow)
441 {
442     IWebBrowser2 *wb = NULL;
443     MSG msg;
444     HRESULT hres;
445
446     TRACE("%s %d\n", debugstr_a(szCommandLine), nShowWindow);
447
448     if(*szCommandLine == '-' || *szCommandLine == '/') {
449         if(!strcasecmp(szCommandLine+1, "regserver"))
450             return register_iexplore(TRUE);
451         if(!strcasecmp(szCommandLine+1, "unregserver"))
452             return register_iexplore(FALSE);
453     }
454
455     CoInitialize(NULL);
456
457     hres = register_class_object(TRUE);
458     if(FAILED(hres)) {
459         CoUninitialize();
460         ExitProcess(1);
461     }
462
463     if(strcasecmp(szCommandLine, "-embedding"))
464         wb = create_ie_window(szCommandLine);
465
466     /* run the message loop for this thread */
467     while (GetMessageW(&msg, 0, 0, 0))
468     {
469         TranslateMessage(&msg);
470         DispatchMessageW(&msg);
471     }
472
473     if(wb)
474         IWebBrowser2_Release(wb);
475
476     register_class_object(FALSE);
477
478     CoUninitialize();
479
480     ExitProcess(0);
481     return 0;
482 }