hhctrl.ocx: Added code for handling tabs.
[wine] / dlls / hhctrl.ocx / help.c
1 /*
2  * Help Viewer Implementation
3  *
4  * Copyright 2005 James Hawkins
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "hhctrl.h"
22
23 #include "wingdi.h"
24 #include "commctrl.h"
25 #include "wininet.h"
26
27 #include "wine/debug.h"
28
29 #include "resource.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
32
33 static LRESULT Help_OnSize(HWND hWnd);
34
35 /* Window type defaults */
36
37 #define WINTYPE_DEFAULT_X           280
38 #define WINTYPE_DEFAULT_Y           100
39 #define WINTYPE_DEFAULT_WIDTH       740
40 #define WINTYPE_DEFAULT_HEIGHT      640
41 #define WINTYPE_DEFAULT_NAVWIDTH    250
42
43 #define TAB_TOP_PADDING     8
44 #define TAB_RIGHT_PADDING   4
45 #define TAB_MARGIN  8
46
47 static const WCHAR szEmpty[] = {0};
48
49 /* Loads a string from the resource file */
50 static LPWSTR HH_LoadString(DWORD dwID)
51 {
52     LPWSTR string = NULL;
53     int iSize;
54
55     iSize = LoadStringW(hhctrl_hinstance, dwID, NULL, 0);
56     iSize += 2; /* some strings (tab text) needs double-null termination */
57
58     string = hhctrl_alloc(iSize * sizeof(WCHAR));
59     LoadStringW(hhctrl_hinstance, dwID, string, iSize);
60
61     return string;
62 }
63
64 BOOL NavigateToUrl(HHInfo *info, LPCWSTR surl)
65 {
66     VARIANT url;
67     HRESULT hres;
68
69     V_VT(&url) = VT_BSTR;
70     V_BSTR(&url) = SysAllocString(surl);
71
72     hres = IWebBrowser2_Navigate2(info->web_browser, &url, 0, 0, 0, 0);
73
74     VariantClear(&url);
75
76     return SUCCEEDED(hres);
77 }
78
79 BOOL NavigateToChm(HHInfo *info, LPCWSTR file, LPCWSTR index)
80 {
81     WCHAR buf[INTERNET_MAX_URL_LENGTH];
82     WCHAR full_path[MAX_PATH];
83     LPWSTR ptr;
84
85     static const WCHAR url_format[] =
86         {'m','k',':','@','M','S','I','T','S','t','o','r','e',':','%','s',':',':','/','%','s',0};
87
88     TRACE("%p %s %s\n", info, debugstr_w(file), debugstr_w(index));
89
90     if (!info->web_browser)
91         return FALSE;
92
93     if(!GetFullPathNameW(file, sizeof(full_path), full_path, NULL)) {
94         WARN("GetFullPathName failed: %u\n", GetLastError());
95         return FALSE;
96     }
97
98     wsprintfW(buf, url_format, full_path, index);
99
100     /* FIXME: HACK */
101     if((ptr = strchrW(buf, '#')))
102        *ptr = 0;
103
104     return NavigateToUrl(info, buf);
105 }
106
107 /* Size Bar */
108
109 #define SIZEBAR_WIDTH   4
110
111 static const WCHAR szSizeBarClass[] = {
112     'H','H',' ','S','i','z','e','B','a','r',0
113 };
114
115 /* Draw the SizeBar */
116 static void SB_OnPaint(HWND hWnd)
117 {
118     PAINTSTRUCT ps;
119     HDC hdc;
120     RECT rc;
121     
122     hdc = BeginPaint(hWnd, &ps);
123
124     GetClientRect(hWnd, &rc);
125
126     /* dark frame */
127     rc.right += 1;
128     rc.bottom -= 1;
129     FrameRect(hdc, &rc, GetStockObject(GRAY_BRUSH));
130
131     /* white highlight */
132     SelectObject(hdc, GetStockObject(WHITE_PEN));
133     MoveToEx(hdc, rc.right, 1, NULL);
134     LineTo(hdc, 1, 1);
135     LineTo(hdc, 1, rc.bottom - 1);
136
137     
138     MoveToEx(hdc, 0, rc.bottom, NULL);
139     LineTo(hdc, rc.right, rc.bottom);
140
141     EndPaint(hWnd, &ps);
142 }
143
144 static void SB_OnLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
145 {
146     SetCapture(hWnd);
147 }
148
149 static void SB_OnLButtonUp(HWND hWnd, WPARAM wParam, LPARAM lParam)
150 {
151     HHInfo *pHHInfo = (HHInfo *)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
152     POINT pt;
153
154     pt.x = (short)LOWORD(lParam);
155     pt.y = (short)HIWORD(lParam);
156
157     /* update the window sizes */
158     pHHInfo->WinType.iNavWidth += pt.x;
159     Help_OnSize(hWnd);
160
161     ReleaseCapture();
162 }
163
164 static void SB_OnMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam)
165 {
166     /* ignore WM_MOUSEMOVE if not dragging the SizeBar */
167     if (!(wParam & MK_LBUTTON))
168         return;
169 }
170
171 static LRESULT CALLBACK SizeBar_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
172 {
173     switch (message)
174     {
175         case WM_LBUTTONDOWN:
176             SB_OnLButtonDown(hWnd, wParam, lParam);
177             break;
178         case WM_LBUTTONUP:
179             SB_OnLButtonUp(hWnd, wParam, lParam);
180             break;
181         case WM_MOUSEMOVE:
182             SB_OnMouseMove(hWnd, wParam, lParam);
183             break;
184         case WM_PAINT:
185             SB_OnPaint(hWnd);
186             break;
187         default:
188             return DefWindowProcW(hWnd, message, wParam, lParam);
189     }
190
191     return 0;
192 }
193
194 static void HH_RegisterSizeBarClass(HHInfo *pHHInfo)
195 {
196     WNDCLASSEXW wcex;
197
198     wcex.cbSize         = sizeof(WNDCLASSEXW);
199     wcex.style          = 0;
200     wcex.lpfnWndProc    = SizeBar_WndProc;
201     wcex.cbClsExtra     = 0;
202     wcex.cbWndExtra     = 0;
203     wcex.hInstance      = hhctrl_hinstance;
204     wcex.hIcon          = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
205     wcex.hCursor        = LoadCursorW(NULL, (LPCWSTR)IDC_SIZEWE);
206     wcex.hbrBackground  = (HBRUSH)(COLOR_MENU + 1);
207     wcex.lpszMenuName   = NULL;
208     wcex.lpszClassName  = szSizeBarClass;
209     wcex.hIconSm        = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
210
211     RegisterClassExW(&wcex);
212 }
213
214 static void SB_GetSizeBarRect(HHInfo *info, RECT *rc)
215 {
216     RECT rectWND, rectTB, rectNP;
217
218     GetClientRect(info->WinType.hwndHelp, &rectWND);
219     GetClientRect(info->WinType.hwndToolBar, &rectTB);
220     GetClientRect(info->WinType.hwndNavigation, &rectNP);
221
222     rc->left = rectNP.right;
223     rc->top = rectTB.bottom;
224     rc->bottom = rectWND.bottom - rectTB.bottom;
225     rc->right = SIZEBAR_WIDTH;
226 }
227
228 static BOOL HH_AddSizeBar(HHInfo *pHHInfo)
229 {
230     HWND hWnd;
231     HWND hwndParent = pHHInfo->WinType.hwndHelp;
232     DWORD dwStyles = WS_CHILDWINDOW | WS_VISIBLE | WS_OVERLAPPED;
233     DWORD dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR;
234     RECT rc;
235
236     SB_GetSizeBarRect(pHHInfo, &rc);
237
238     hWnd = CreateWindowExW(dwExStyles, szSizeBarClass, szEmpty, dwStyles,
239                            rc.left, rc.top, rc.right, rc.bottom,
240                            hwndParent, NULL, hhctrl_hinstance, NULL);
241     if (!hWnd)
242         return FALSE;
243
244     /* store the pointer to the HH info struct */
245     SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)pHHInfo);
246
247     pHHInfo->hwndSizeBar = hWnd;
248     return TRUE;
249 }
250
251 /* Child Window */
252
253 static const WCHAR szChildClass[] = {
254     'H','H',' ','C','h','i','l','d',0
255 };
256
257 static LRESULT Child_OnPaint(HWND hWnd)
258 {
259     PAINTSTRUCT ps;
260     HDC hdc;
261     RECT rc;
262
263     hdc = BeginPaint(hWnd, &ps);
264
265     /* Only paint the Navigation pane, identified by the fact
266      * that it has a child window
267      */
268     if (GetWindow(hWnd, GW_CHILD))
269     {
270         GetClientRect(hWnd, &rc);
271
272         /* set the border color */
273         SelectObject(hdc, GetStockObject(DC_PEN));
274         SetDCPenColor(hdc, GetSysColor(COLOR_BTNSHADOW));
275
276         /* Draw the top border */
277         LineTo(hdc, rc.right, 0);
278
279         SelectObject(hdc, GetStockObject(WHITE_PEN));
280         MoveToEx(hdc, 0, 1, NULL);
281         LineTo(hdc, rc.right, 1);
282     }
283
284     EndPaint(hWnd, &ps);
285
286     return 0;
287 }
288
289 static void ResizeTabChild(HHInfo *info, HWND hwnd)
290 {
291     RECT rect, tabrc;
292     DWORD cnt;
293
294     GetClientRect(info->WinType.hwndNavigation, &rect);
295     SendMessageW(info->hwndTabCtrl, TCM_GETITEMRECT, 0, (LPARAM)&tabrc);
296     cnt = SendMessageW(info->hwndTabCtrl, TCM_GETROWCOUNT, 0, 0);
297
298     rect.left = TAB_MARGIN;
299     rect.top = TAB_TOP_PADDING + cnt*(tabrc.bottom-tabrc.top) + TAB_MARGIN;
300     rect.right -= TAB_RIGHT_PADDING + TAB_MARGIN;
301     rect.bottom -= TAB_MARGIN;
302
303     SetWindowPos(hwnd, NULL, rect.left, rect.top, rect.right-rect.left,
304                  rect.bottom-rect.top, SWP_NOZORDER | SWP_NOACTIVATE);
305 }
306
307 static LRESULT Child_OnSize(HWND hwnd)
308 {
309     HHInfo *info = (HHInfo*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
310     RECT rect;
311
312     if(!info || hwnd != info->WinType.hwndNavigation)
313         return 0;
314
315     GetClientRect(hwnd, &rect);
316     SetWindowPos(info->hwndTabCtrl, HWND_TOP, 0, 0,
317                  rect.right - TAB_RIGHT_PADDING,
318                  rect.bottom - TAB_TOP_PADDING, SWP_NOMOVE);
319
320     ResizeTabChild(info, info->tabs[TAB_CONTENTS].hwnd);
321     return 0;
322 }
323
324 static LRESULT OnTabChange(HWND hwnd)
325 {
326     HHInfo *info = (HHInfo*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
327
328     TRACE("%p\n", hwnd);
329
330     if (!info)
331         return 0;
332
333     if(info->tabs[info->current_tab].hwnd)
334         ShowWindow(info->tabs[info->current_tab].hwnd, SW_HIDE);
335
336     info->current_tab = SendMessageW(info->hwndTabCtrl, TCM_GETCURSEL, 0, 0);
337
338     if(info->tabs[info->current_tab].hwnd)
339         ShowWindow(info->tabs[info->current_tab].hwnd, SW_SHOW);
340
341     return 0;
342 }
343
344 static LRESULT CALLBACK Child_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
345 {
346     switch (message)
347     {
348     case WM_PAINT:
349         return Child_OnPaint(hWnd);
350     case WM_SIZE:
351         return Child_OnSize(hWnd);
352     case WM_NOTIFY: {
353         NMHDR *nmhdr = (NMHDR*)lParam;
354         switch(nmhdr->code) {
355         case TCN_SELCHANGE:
356             return OnTabChange(hWnd);
357         }
358         break;
359     }
360     default:
361         return DefWindowProcW(hWnd, message, wParam, lParam);
362     }
363
364     return 0;
365 }
366
367 static void HH_RegisterChildWndClass(HHInfo *pHHInfo)
368 {
369     WNDCLASSEXW wcex;
370
371     wcex.cbSize         = sizeof(WNDCLASSEXW);
372     wcex.style          = 0;
373     wcex.lpfnWndProc    = Child_WndProc;
374     wcex.cbClsExtra     = 0;
375     wcex.cbWndExtra     = 0;
376     wcex.hInstance      = hhctrl_hinstance;
377     wcex.hIcon          = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
378     wcex.hCursor        = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
379     wcex.hbrBackground  = (HBRUSH)(COLOR_BTNFACE + 1);
380     wcex.lpszMenuName   = NULL;
381     wcex.lpszClassName  = szChildClass;
382     wcex.hIconSm        = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
383
384     RegisterClassExW(&wcex);
385 }
386
387 /* Toolbar */
388
389 #define ICON_SIZE   20
390
391 static void TB_OnClick(HWND hWnd, DWORD dwID)
392 {
393     HHInfo *info = (HHInfo *)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
394
395     switch (dwID)
396     {
397         case IDTB_STOP:
398             DoPageAction(info, WB_STOP);
399             break;
400         case IDTB_REFRESH:
401             DoPageAction(info, WB_REFRESH);
402             break;
403         case IDTB_BACK:
404             DoPageAction(info, WB_GOBACK);
405             break;
406         case IDTB_HOME:
407             NavigateToChm(info, info->pCHMInfo->szFile, info->WinType.pszHome);
408             break;
409         case IDTB_FORWARD:
410             DoPageAction(info, WB_GOFORWARD);
411             break;
412         case IDTB_EXPAND:
413         case IDTB_CONTRACT:
414         case IDTB_SYNC:
415         case IDTB_PRINT:
416         case IDTB_OPTIONS:
417         case IDTB_BROWSE_FWD:
418         case IDTB_BROWSE_BACK:
419         case IDTB_JUMP1:
420         case IDTB_JUMP2:
421         case IDTB_CUSTOMIZE:
422         case IDTB_ZOOM:
423         case IDTB_TOC_NEXT:
424         case IDTB_TOC_PREV:
425             break;
426     }
427 }
428
429 static void TB_AddButton(TBBUTTON *pButtons, DWORD dwIndex, DWORD dwID)
430 {
431     /* FIXME: Load the correct button bitmaps */
432     pButtons[dwIndex].iBitmap = STD_PRINT;
433     pButtons[dwIndex].idCommand = dwID;
434     pButtons[dwIndex].fsState = TBSTATE_ENABLED;
435     pButtons[dwIndex].fsStyle = BTNS_BUTTON;
436     pButtons[dwIndex].dwData = 0;
437     pButtons[dwIndex].iString = 0;
438 }
439
440 static void TB_AddButtonsFromFlags(TBBUTTON *pButtons, DWORD dwButtonFlags, LPDWORD pdwNumButtons)
441 {
442     *pdwNumButtons = 0;
443
444     if (dwButtonFlags & HHWIN_BUTTON_EXPAND)
445         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_EXPAND);
446
447     if (dwButtonFlags & HHWIN_BUTTON_BACK)
448         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_BACK);
449
450     if (dwButtonFlags & HHWIN_BUTTON_FORWARD)
451         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_FORWARD);
452
453     if (dwButtonFlags & HHWIN_BUTTON_STOP)
454         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_STOP);
455
456     if (dwButtonFlags & HHWIN_BUTTON_REFRESH)
457         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_REFRESH);
458
459     if (dwButtonFlags & HHWIN_BUTTON_HOME)
460         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_HOME);
461
462     if (dwButtonFlags & HHWIN_BUTTON_SYNC)
463         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_SYNC);
464
465     if (dwButtonFlags & HHWIN_BUTTON_OPTIONS)
466         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_OPTIONS);
467
468     if (dwButtonFlags & HHWIN_BUTTON_PRINT)
469         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_PRINT);
470
471     if (dwButtonFlags & HHWIN_BUTTON_JUMP1)
472         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_JUMP1);
473
474     if (dwButtonFlags & HHWIN_BUTTON_JUMP2)
475         TB_AddButton(pButtons,(*pdwNumButtons)++, IDTB_JUMP2);
476
477     if (dwButtonFlags & HHWIN_BUTTON_ZOOM)
478         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_ZOOM);
479
480     if (dwButtonFlags & HHWIN_BUTTON_TOC_NEXT)
481         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_TOC_NEXT);
482
483     if (dwButtonFlags & HHWIN_BUTTON_TOC_PREV)
484         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_TOC_PREV);
485 }
486
487 static BOOL HH_AddToolbar(HHInfo *pHHInfo)
488 {
489     HWND hToolbar;
490     HWND hwndParent = pHHInfo->WinType.hwndHelp;
491     DWORD toolbarFlags;
492     TBBUTTON buttons[IDTB_TOC_PREV - IDTB_EXPAND];
493     TBADDBITMAP tbAB;
494     DWORD dwStyles, dwExStyles;
495     DWORD dwNumButtons, dwIndex;
496
497     if (pHHInfo->WinType.fsWinProperties & HHWIN_PARAM_TB_FLAGS)
498         toolbarFlags = pHHInfo->WinType.fsToolBarFlags;
499     else
500         toolbarFlags = HHWIN_DEF_BUTTONS;
501
502     TB_AddButtonsFromFlags(buttons, toolbarFlags, &dwNumButtons);
503
504     dwStyles = WS_CHILDWINDOW | WS_VISIBLE | TBSTYLE_FLAT |
505                TBSTYLE_WRAPABLE | TBSTYLE_TOOLTIPS | CCS_NODIVIDER;
506     dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR;
507
508     hToolbar = CreateWindowExW(dwExStyles, TOOLBARCLASSNAMEW, NULL, dwStyles,
509                                0, 0, 0, 0, hwndParent, NULL,
510                                hhctrl_hinstance, NULL);
511     if (!hToolbar)
512         return FALSE;
513
514     SendMessageW(hToolbar, TB_SETBITMAPSIZE, 0, MAKELONG(ICON_SIZE, ICON_SIZE));
515     SendMessageW(hToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
516     SendMessageW(hToolbar, WM_SETFONT, (WPARAM)pHHInfo->hFont, TRUE);
517
518     /* FIXME: Load correct icons for all buttons */
519     tbAB.hInst = HINST_COMMCTRL;
520     tbAB.nID = IDB_STD_LARGE_COLOR;
521     SendMessageW(hToolbar, TB_ADDBITMAP, 0, (LPARAM)&tbAB);
522
523     for (dwIndex = 0; dwIndex < dwNumButtons; dwIndex++)
524     {
525         LPWSTR szBuf = HH_LoadString(buttons[dwIndex].idCommand);
526         DWORD dwLen = strlenW(szBuf);
527         szBuf[dwLen + 2] = 0; /* Double-null terminate */
528
529         buttons[dwIndex].iString = (DWORD)SendMessageW(hToolbar, TB_ADDSTRINGW, 0, (LPARAM)szBuf);
530         hhctrl_free(szBuf);
531     }
532
533     SendMessageW(hToolbar, TB_ADDBUTTONSW, dwNumButtons, (LPARAM)&buttons);
534     SendMessageW(hToolbar, TB_AUTOSIZE, 0, 0);
535     ShowWindow(hToolbar, SW_SHOW);
536
537     pHHInfo->WinType.hwndToolBar = hToolbar;
538     return TRUE;
539 }
540
541 /* Navigation Pane */
542
543 static void NP_GetNavigationRect(HHInfo *pHHInfo, RECT *rc)
544 {
545     HWND hwndParent = pHHInfo->WinType.hwndHelp;
546     HWND hwndToolbar = pHHInfo->WinType.hwndToolBar;
547     RECT rectWND, rectTB;
548
549     GetClientRect(hwndParent, &rectWND);
550     GetClientRect(hwndToolbar, &rectTB);
551
552     rc->left = 0;
553     rc->top = rectTB.bottom;
554     rc->bottom = rectWND.bottom - rectTB.bottom;
555
556     if (!(pHHInfo->WinType.fsValidMembers & HHWIN_PARAM_NAV_WIDTH) &&
557           pHHInfo->WinType.iNavWidth == 0)
558     {
559         pHHInfo->WinType.iNavWidth = WINTYPE_DEFAULT_NAVWIDTH;
560     }
561
562     rc->right = pHHInfo->WinType.iNavWidth;
563 }
564
565 static DWORD NP_CreateTab(HINSTANCE hInstance, HWND hwndTabCtrl, DWORD index)
566 {
567     TCITEMW tie;
568     LPWSTR tabText = HH_LoadString(index);
569     DWORD ret;
570
571     tie.mask = TCIF_TEXT;
572     tie.pszText = tabText;
573
574     ret = SendMessageW( hwndTabCtrl, TCM_INSERTITEMW, index, (LPARAM)&tie );
575
576     hhctrl_free(tabText);
577     return ret;
578 }
579
580 static BOOL HH_AddNavigationPane(HHInfo *info)
581 {
582     HWND hWnd, hwndTabCtrl;
583     HWND hwndParent = info->WinType.hwndHelp;
584     DWORD dwStyles = WS_CHILDWINDOW | WS_VISIBLE;
585     DWORD dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR;
586     RECT rc;
587
588     NP_GetNavigationRect(info, &rc);
589
590     hWnd = CreateWindowExW(dwExStyles, szChildClass, szEmpty, dwStyles,
591                            rc.left, rc.top, rc.right, rc.bottom,
592                            hwndParent, NULL, hhctrl_hinstance, NULL);
593     if (!hWnd)
594         return FALSE;
595
596     SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)info);
597
598     hwndTabCtrl = CreateWindowExW(dwExStyles, WC_TABCONTROLW, szEmpty, dwStyles,
599                                   0, TAB_TOP_PADDING,
600                                   rc.right - TAB_RIGHT_PADDING,
601                                   rc.bottom - TAB_TOP_PADDING,
602                                   hWnd, NULL, hhctrl_hinstance, NULL);
603     if (!hwndTabCtrl)
604         return FALSE;
605
606     if (*info->WinType.pszToc)
607         info->tabs[TAB_CONTENTS].id = NP_CreateTab(hhctrl_hinstance, hwndTabCtrl, IDS_CONTENTS);
608
609     if (*info->WinType.pszIndex)
610         info->tabs[TAB_INDEX].id = NP_CreateTab(hhctrl_hinstance, hwndTabCtrl, IDS_INDEX);
611
612     if (info->WinType.fsWinProperties & HHWIN_PROP_TAB_SEARCH)
613         info->tabs[TAB_SEARCH].id = NP_CreateTab(hhctrl_hinstance, hwndTabCtrl, IDS_SEARCH);
614
615     if (info->WinType.fsWinProperties & HHWIN_PROP_TAB_FAVORITES)
616         info->tabs[TAB_FAVORITES].id = NP_CreateTab(hhctrl_hinstance, hwndTabCtrl, IDS_FAVORITES);
617
618     SendMessageW(hwndTabCtrl, WM_SETFONT, (WPARAM)info->hFont, TRUE);
619
620     info->hwndTabCtrl = hwndTabCtrl;
621     info->WinType.hwndNavigation = hWnd;
622     return TRUE;
623 }
624
625 /* HTML Pane */
626
627 static void HP_GetHTMLRect(HHInfo *info, RECT *rc)
628 {
629     RECT rectTB, rectWND, rectNP, rectSB;
630
631     GetClientRect(info->WinType.hwndHelp, &rectWND);
632     GetClientRect(info->WinType.hwndToolBar, &rectTB);
633     GetClientRect(info->WinType.hwndNavigation, &rectNP);
634     GetClientRect(info->hwndSizeBar, &rectSB);
635
636     rc->left = rectNP.right + rectSB.right;
637     rc->top = rectTB.bottom;
638     rc->right = rectWND.right - rc->left;
639     rc->bottom = rectWND.bottom - rectTB.bottom;
640 }
641
642 static BOOL HH_AddHTMLPane(HHInfo *pHHInfo)
643 {
644     HWND hWnd;
645     HWND hwndParent = pHHInfo->WinType.hwndHelp;
646     DWORD dwStyles = WS_CHILDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN;
647     DWORD dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_CLIENTEDGE;
648     RECT rc;
649
650     HP_GetHTMLRect(pHHInfo, &rc);
651
652     hWnd = CreateWindowExW(dwExStyles, szChildClass, szEmpty, dwStyles,
653                            rc.left, rc.top, rc.right, rc.bottom,
654                            hwndParent, NULL, hhctrl_hinstance, NULL);
655     if (!hWnd)
656         return FALSE;
657
658     if (!InitWebBrowser(pHHInfo, hWnd))
659         return FALSE;
660
661     /* store the pointer to the HH info struct */
662     SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)pHHInfo);
663
664     ShowWindow(hWnd, SW_SHOW);
665     UpdateWindow(hWnd);
666
667     pHHInfo->WinType.hwndHTML = hWnd;
668     return TRUE;
669 }
670
671 /* Viewer Window */
672
673 static LRESULT Help_OnSize(HWND hWnd)
674 {
675     HHInfo *pHHInfo = (HHInfo *)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
676     DWORD dwSize;
677     RECT rc;
678
679     if (!pHHInfo)
680         return 0;
681
682     NP_GetNavigationRect(pHHInfo, &rc);
683     SetWindowPos(pHHInfo->WinType.hwndNavigation, HWND_TOP, 0, 0,
684                  rc.right, rc.bottom, SWP_NOMOVE);
685
686     SB_GetSizeBarRect(pHHInfo, &rc);
687     SetWindowPos(pHHInfo->hwndSizeBar, HWND_TOP, rc.left, rc.top,
688                  rc.right, rc.bottom, SWP_SHOWWINDOW);
689
690     HP_GetHTMLRect(pHHInfo, &rc);
691     SetWindowPos(pHHInfo->WinType.hwndHTML, HWND_TOP, rc.left, rc.top,
692                  rc.right, rc.bottom, SWP_SHOWWINDOW);
693
694     /* Resize browser window taking the frame size into account */
695     dwSize = GetSystemMetrics(SM_CXFRAME);
696     ResizeWebBrowser(pHHInfo, rc.right - dwSize, rc.bottom - dwSize);
697
698     return 0;
699 }
700
701 static LRESULT CALLBACK Help_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
702 {
703     switch (message)
704     {
705     case WM_COMMAND:
706         if (HIWORD(wParam) == BN_CLICKED)
707             TB_OnClick(hWnd, LOWORD(wParam));
708         break;
709     case WM_SIZE:
710         return Help_OnSize(hWnd);
711     case WM_CLOSE:
712         ReleaseHelpViewer((HHInfo *)GetWindowLongPtrW(hWnd, GWLP_USERDATA));
713         return 0;
714     case WM_DESTROY:
715         if(hh_process)
716             PostQuitMessage(0);
717         break;
718
719     default:
720         return DefWindowProcW(hWnd, message, wParam, lParam);
721     }
722
723     return 0;
724 }
725
726 static BOOL HH_CreateHelpWindow(HHInfo *info)
727 {
728     HWND hWnd;
729     RECT winPos = info->WinType.rcWindowPos;
730     WNDCLASSEXW wcex;
731     DWORD dwStyles, dwExStyles;
732     DWORD x, y, width, height;
733
734     static const WCHAR windowClassW[] = {
735         'H','H',' ', 'P','a','r','e','n','t',0
736     };
737
738     wcex.cbSize         = sizeof(WNDCLASSEXW);
739     wcex.style          = CS_HREDRAW | CS_VREDRAW;
740     wcex.lpfnWndProc    = Help_WndProc;
741     wcex.cbClsExtra     = 0;
742     wcex.cbWndExtra     = 0;
743     wcex.hInstance      = hhctrl_hinstance;
744     wcex.hIcon          = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
745     wcex.hCursor        = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
746     wcex.hbrBackground  = (HBRUSH)(COLOR_MENU + 1);
747     wcex.lpszMenuName   = NULL;
748     wcex.lpszClassName  = windowClassW;
749     wcex.hIconSm        = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
750
751     RegisterClassExW(&wcex);
752
753     /* Read in window parameters if available */
754     if (info->WinType.fsValidMembers & HHWIN_PARAM_STYLES)
755         dwStyles = info->WinType.dwStyles;
756     else
757         dwStyles = WS_OVERLAPPEDWINDOW | WS_VISIBLE |
758                    WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
759
760     if (info->WinType.fsValidMembers & HHWIN_PARAM_EXSTYLES)
761         dwExStyles = info->WinType.dwExStyles;
762     else
763         dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_APPWINDOW |
764                      WS_EX_WINDOWEDGE | WS_EX_RIGHTSCROLLBAR;
765
766     if (info->WinType.fsValidMembers & HHWIN_PARAM_RECT)
767     {
768         x = winPos.left;
769         y = winPos.top;
770         width = winPos.right - x;
771         height = winPos.bottom - y;
772     }
773     else
774     {
775         x = WINTYPE_DEFAULT_X;
776         y = WINTYPE_DEFAULT_Y;
777         width = WINTYPE_DEFAULT_WIDTH;
778         height = WINTYPE_DEFAULT_HEIGHT;
779     }
780
781     hWnd = CreateWindowExW(dwExStyles, windowClassW, info->WinType.pszCaption,
782                            dwStyles, x, y, width, height, NULL, NULL, hhctrl_hinstance, NULL);
783     if (!hWnd)
784         return FALSE;
785
786     ShowWindow(hWnd, SW_SHOW);
787     UpdateWindow(hWnd);
788
789     /* store the pointer to the HH info struct */
790     SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)info);
791
792     info->WinType.hwndHelp = hWnd;
793     return TRUE;
794 }
795
796 static void HH_CreateFont(HHInfo *pHHInfo)
797 {
798     LOGFONTW lf;
799
800     GetObjectW(GetStockObject(ANSI_VAR_FONT), sizeof(LOGFONTW), &lf);
801     lf.lfWeight = FW_NORMAL;
802     lf.lfItalic = FALSE;
803     lf.lfUnderline = FALSE;
804
805     pHHInfo->hFont = CreateFontIndirectW(&lf);
806 }
807
808 static void HH_InitRequiredControls(DWORD dwControls)
809 {
810     INITCOMMONCONTROLSEX icex;
811
812     icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
813     icex.dwICC = dwControls;
814     InitCommonControlsEx(&icex);
815 }
816
817 /* Creates the whole package */
818 static BOOL CreateViewer(HHInfo *pHHInfo)
819 {
820     HH_CreateFont(pHHInfo);
821
822     if (!HH_CreateHelpWindow(pHHInfo))
823         return FALSE;
824
825     HH_InitRequiredControls(ICC_BAR_CLASSES);
826
827     if (!HH_AddToolbar(pHHInfo))
828         return FALSE;
829
830     HH_RegisterChildWndClass(pHHInfo);
831
832     if (!HH_AddNavigationPane(pHHInfo))
833         return FALSE;
834
835     HH_RegisterSizeBarClass(pHHInfo);
836
837     if (!HH_AddSizeBar(pHHInfo))
838         return FALSE;
839
840     if (!HH_AddHTMLPane(pHHInfo))
841         return FALSE;
842
843     return TRUE;
844 }
845
846 void ReleaseHelpViewer(HHInfo *info)
847 {
848     if (!info)
849         return;
850
851     /* Free allocated strings */
852     hhctrl_free((LPWSTR)info->WinType.pszType);
853     hhctrl_free((LPWSTR)info->WinType.pszCaption);
854     hhctrl_free((LPWSTR)info->WinType.pszToc);
855     hhctrl_free((LPWSTR)info->WinType.pszIndex);
856     hhctrl_free((LPWSTR)info->WinType.pszFile);
857     hhctrl_free((LPWSTR)info->WinType.pszHome);
858     hhctrl_free((LPWSTR)info->WinType.pszJump1);
859     hhctrl_free((LPWSTR)info->WinType.pszJump2);
860     hhctrl_free((LPWSTR)info->WinType.pszUrlJump1);
861     hhctrl_free((LPWSTR)info->WinType.pszUrlJump2);
862
863     if (info->pCHMInfo)
864         CloseCHM(info->pCHMInfo);
865
866     ReleaseWebBrowser(info);
867
868     if(info->WinType.hwndHelp)
869         DestroyWindow(info->WinType.hwndHelp);
870
871     hhctrl_free(info);
872     OleUninitialize();
873 }
874
875 HHInfo *CreateHelpViewer(LPCWSTR filename)
876 {
877     HHInfo *info = hhctrl_alloc_zero(sizeof(HHInfo));
878
879     OleInitialize(NULL);
880
881     info->pCHMInfo = OpenCHM(filename);
882     if(!info->pCHMInfo) {
883         ReleaseHelpViewer(info);
884         return NULL;
885     }
886
887     if (!LoadWinTypeFromCHM(info->pCHMInfo, &info->WinType)) {
888         ReleaseHelpViewer(info);
889         return NULL;
890     }
891
892     if(!CreateViewer(info)) {
893         ReleaseHelpViewer(info);
894         return NULL;
895     }
896
897     return info;
898 }